2014-04-15 3 views
1

애그리 게이터 유형을 클래스로 승격하려고하는데이를 수행하는 방법을 알 수 없습니다.유형 종속성이있는 여러 매개 변수 클래스

data Aggregator a b = Aggregator { aggregate :: [a] -> b } 
minMax = Aggregator (\xs -> (minimum xs, maximum)) 

와 나는 같은 싶습니다 :

나는이가 분명히 작동하지 않습니다

data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b) 

instance Aggregator (DoubleAggregator a b) where 
    aggregate (DoubleAggregator f g) as = (f as, g as) 

: 같은

class Aggregator (g a b) where 
    aggregate :: g -> [a] -> b 

그래서 내가 할 수있는 일을. 나는 MultipleParamTypeClasses 또는 FamilyTypes 중 하나가 필요하다고 생각하지만, 알아낼 수는 없습니다. 나는 특히 클래스를 필요로하지 않고 단지 data Aggregator a b ...으로 할 수 있다고 생각합니다. 그러나 나는 클래스와 함께하는 방법에 여전히 관심이 있습니다. 어떻게해야합니까?

+0

난 당신이 취급에 특별히 관심이있는 알을 사용하여 더 복잡한 수집기로를 구성하는 이것은 클래스를 가지고 있지만,'data Aggregator a b '를 사용했다 할지라도'fold'문제와 비슷하게 들린다. ... ', 비어있는 목록이 올바르게 처리되지 않을 것 같은 것처럼 보입니다. '모노 이드 (Monoid) '와 폴드 (fold)를 살펴볼 수 있습니다. – unfoldr

답변

2

출력이 (b,b)이지만 b이어야한다고 클래스 선언에 명시된대로 DoubleAggregator에 대한 인스턴스의 형식이 올바르지 않습니다. 그래서 당신은 아마도 바람직하지 않은 일에 당신이 개 b의 병합하는 방법을 알아낼 또는 출력 유형을 확인하여 집계 유형에 따라 다음 중 하나를 클래스가 언급하지 않는

{-# LANGUAGE TypeFamilies #-} 

class Aggregator g where 
    type Result x y z 
    aggregate :: g a b -> [a] -> Result g a b 

data SingleAggregator a b = SingleAggregator ([a] -> b) 
instance Aggregator SingleAggregator where 
    type Result SingleAggregator a b = b 
    aggregate (SingleAggregator f) = f 

data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b) 
instance Aggregator DoubleAggregator where 
    type Result DoubleAggregator a b = (b,b) 
    aggregate (DoubleAggregator f g) as = (f as, g as) 

하는 것으로 a 또는 b 왜냐하면 그것들은 자유롭게 어떤 타입이 될 수도 있고 애그리 게이터 타입에 의존하지 않기 때문이다. 유형 Result은 집합 자의 유형과 집합 자 내부의 유형의 세 가지 기능입니다. 또한 MultiParamTypeClassesFunctionalDependancies하여이 작업을 수행 할 수 있습니다

class Aggregator g where 
    type Result x y 
    aggregate :: g a b -> [a] -> Result g b 

.... 

    type Result SingleAggregator b = b 

.... 

    type Result DoubleAggregator b = (b, b) 

하지만 더 복잡하고 종류가 어렵습니다 : 그것은 당신의 결과는 당신이 뭔가를 기록 할 수있는 경우에 입력 타입, a에 의존하지 않을 가능성이 있습니다 읽으려면 내 의견으로는 더 나쁜 해결책이며, 완성을 위해서만 포함시켜야합니다.

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-} 

class Aggregator g a b r | g a b -> r where 
    aggregate :: g a b -> [a] -> r 

data SingleAggregator a b = SingleAggregator ([a] -> b) 
instance Aggregator SingleAggregator a b b where 
    aggregate (SingleAggregator f) = f 

data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b) 
instance Aggregator DoubleAggregator a b (b,b) where 
    aggregate (DoubleAggregator f g) as = (f as, g as) 

주요 차이점은 기능적 종속성입니다. | g a b -> r; 일부 g a b에 대해 존재할 수있는 고유 한 r이 있습니다.

+0

두 가지 방법을 보여 주셔서 감사합니다. – mb14

4

형식 패밀리 또는 기능 종속성 (user2407038의 대답과 유사) 외에도 다른 대체 방법은 GADT을 사용하는 것입니다.

{-# LANGUAGE GADTs #-} 

class Aggregator g where 
    aggregate :: g a b -> [a] -> b 


data SingleAggregator a b = SingleAggregator ([a] -> b) 

data DoubleAggregator a b where 
    DoubleAggregator :: ([a] -> b) -> ([a] -> b) -> DoubleAggregator a (b, b) 


instance Aggregator SingleAggregator where 
    aggregate (SingleAggregator f) as = f as 

instance Aggregator DoubleAggregator where 
    aggregate (DoubleAggregator f g) as = (f as, g as) 

이 방법이 더 나은지 나쁜지는 귀하의 정확한 사용 사례에 따라 다르지만 실제로 유형 클래스 확장보다 훨씬 간단합니다.

GADT 접근법을 사용하면 하나의 대수 형식을 원할 경우 유형 클래스를 모두 삭제할 수 있습니다.

data Aggregator a b where 
    SingleAggregator :: ([a] -> b) -> Aggregator a b 
    DoubleAggregator :: ([a] -> b) -> ([a] -> b) -> Aggregator a (b, b) 

aggregate :: Aggregator a b -> [a] -> b 
aggregate (SingleAggregator f) as = f as 
aggregate (DoubleAggregator f g) as = (f as, g as) 

그러나이 접근의 아마도 가장 "하스켈-Y"방법은 단지 하나의 Aggregator newtype은이 있고 Applicative 타입 클래스와 그 작성 가능하게했다.예를 들어 :

import Control.Applicative 

newtype Aggregator a b = Aggregator { aggregate :: [a] -> b } 

instance Functor (Aggregator a) where 
    fmap f (Aggregator g) = Aggregator (f . g) 

instance Applicative (Aggregator a) where 
    pure = Aggregator . const 
    Aggregator f <*> Aggregator x = Aggregator $ f <*> x 

이제

minAgg = Aggregator minimum 
maxAgg = Aggregator maximum 

같은 간단한 수집기를 정의 할 수 있습니다 다음 Applicative 인터페이스

minMax = liftA2 (,) minAgg maxAgg 
+0

나는 그것을 정말로 좋아한다! 고마워요. (마침내 GADT가 무엇인지 이해했을 것입니다. :) – mb14

+0

감사합니다. :) 나는 또한 이와 같은 문제에서 가장 일반적으로 사용되는 패턴이라고 생각되는 Applicatives를 사용하는 예제를 추가했습니다. – shang