2017-12-31 31 views
-1

여기에 집어 넣었습니다. 과 함께 할 수있는 방법을 알아낼 수 없습니다.newtype을 모나드의 인스턴스로 만드는 방법

newtype Val a = Val {getVal :: [a]} deriving (Show) 

instance Monad Val where 
    return = Val 
    (>>=) (Val {getVal = l}) f = map f l 

나는 다음과 같이 작성하는 경우 :

instance Monad Val where 
return = Val 
(>>=) (Val {getVal = l}) f = {getVal = map f l} 

는 내가 오류가 발생하며 parse error on input { 을 말한다.

newtype을 Monad의 인스턴스로 만들려면 어떻게해야합니까? >>=의 정의에 Val의 인스턴스를 생성 할 때

+3

'return'의 정의는 타입 체크가 아닙니다, 그렇습니까? – Carl

답변

1

GeneralizedNewtypeDeriving이 가장 편리한 옵션입니다.

더 긴 대안은 안전한 강제 변환을 사용하여 하스켈에 자동으로 모든 포장/포장 풀기를 수행하도록 요청하는 것입니다. 불행하게도, 이것은 수동 타입 주석과 인자가 필요하다. 왜냐하면 다른 타입들은 automagic 강제 변환이 작동하기에는 너무 일반적이기 때문이다.

매우 편리하지는 않지만 거기에 있습니다.

{-# LANGUAGE InstanceSigs, ScopedTypeVariables, TypeApplications #-} 

import Data.Coerce 

newtype Val a = Val {getVal :: [a]} deriving (Show) 

instance Functor Val where 
    fmap :: forall a b. (a -> b) -> Val a -> Val b 
    fmap = coerce (fmap @ [] @ a @ b) 

instance Applicative Val where 
    pure :: forall a. a -> Val a 
    pure = coerce (pure @ [] @ a) 
    (<*>) :: forall a b. Val (a -> b) -> Val a -> Val b 
    (<*>) = coerce ((<*>) @ [] @ a @ b) 

instance Monad Val where 
    return :: forall a. a -> Val a 
    return = coerce (return @ [] @ a) 
    (>>=) :: forall a b. Val a -> (a -> Val b) -> Val b 
    (>>=) = coerce ((>>=) @ [] @ a @ b) 

또 다른 옵션은 수동으로 위의 코드에서 자동화 된 coerce, 풀기/포장을하는 것입니다. 그래도 꽤 지루하고 성가시다. 그럼에도 불구하고 형식 주석이나 인수가 필요하지 않으므로 적어도 주석을 첨부하지 않아도됩니다.

+0

이것은 내가 배우는 것보다 진보 된 것이지만 나는 새로운 것을 배웠기 때문에 기쁘다. :) –

3

당신은 생성자를 제공해야합니다

(>>=) (Val {getVal = l}) f = Val {getVal = map f l} 

하스켈은 "무료"기록이없는, 방법은, 말하자면, PureScript는 않습니다. Haskell에서 { x = y, v = u }은 레코드를 생성하기위한 유효한 구문이 아닙니다. 항상 생성자를 제공해야합니다. C { x = y, v = u }.

+2

이것은 구문 오류를 수정하지만'return'과'(>> =)'모두에 대해 유형 오류가 계속있을 것입니다. – 4castle

+0

실제로. 나는 그 질문에 언급 된 것에 만 답하고 더 이상 보지 않았다. 나는이 답을 완성을 위해 여기에 남겨 둘 것이다. –

5

여기에는 몇 가지 문제가 있습니다. 먼저, 중괄호 앞에 레코드 이름을 지정해야합니다. 그래서 지금

(>>=) (Val {getVal = l}) f = Val {getVal = map f l} 

를 원하는, 당신의 기능 중 어느 것도 지금의 유형 체킹 것입니다. 그러나 이미 존재하는리스트 모나드 인스턴스에 위임하기 때문에 수정이 너무 어렵지 않습니다. return은 가깝지만 결과를 목록으로 묶어야합니다.

return x = Val [x] 

마찬가지로, map을 레코드 내에 포함하지 않을 것입니다. 당신은 (>>=)의 목록 모나드 버전을 원합니다.

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= f} 

불행하게도, 이것은 아직도 아주의 유형 체킹, f 이후 Val하지 []를 반환하도록 설계되어 있습니다. 우리는 모나드 연산 내부에서 수정해야합니다.

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= getVal . f} 

그 후, 당신은 가능성이 ApplicativeMonad의 슈퍼 클래스 것에 대해 오류를 얻을 수 있습니다. 이는 일부 도우미 기능의 간단한 적용으로 해결할 수 있습니다.

import Control.Monad 

-- ... 

instance Functor Val where 
    fmap = liftM 

instance Applicative Val where 
    pure = return 
    (<*>) = ap 

이제 모든 사항이 컴파일되어야합니다. 당신이 GHC를 사용하는 경우 보조 노트로


은 자동으로 newtype 데이터 유형에 대한 당신에게 인스턴스를 줄 것이다 GeneralizedNewtypeDeriving라는 편리한 기능이있다. 당신은 newtype하지 data에 너무

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

newtype Val a = Val {getVal :: [a]} 
    deriving (Show, Functor, Applicative, Monad) 

이 유일한 작품처럼 사용할 수 있으며, 그것은 단지 GHC에서 작동하므로 휴대용에서 컴파일러 될 코드를하려는 경우 사용하지 마십시오.