Either의 양쪽에 다형성 함수를 적용하는 방법은 무엇입니까?

Dec 06 2020

나는 이것을 시도했다 :

type TestT = Either Int Float

testM :: (a -> a) -> TestT -> TestT
testM f (Left x) = Left (f x)
testM f (Right x) = Right (f x)

하지만 작동하지 않습니다.이 작업을 수행 할 방법이 있습니까? 나는 주위를 둘러 보았고 비슷한 모든 것이 정말 복잡하고 제한적이었습니다.

요청 된 오류 메시지 :

Main.hs:101:28: error:
    • Couldn't match expected type ‘a’ with actual type ‘Int’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          testM :: forall a. (a -> a) -> TestT -> TestT
        at Main.hs:100:1-35
    • In the first argument of ‘f’, namely ‘x’
      In the first argument of ‘Left’, namely ‘(f x)’
      In the expression: Left (f x)
    • Relevant bindings include
        f :: a -> a (bound at Main.hs:101:7)
        testM :: (a -> a) -> TestT -> TestT (bound at Main.hs:101:1)

답변

3 jpmarinier Dec 06 2020 at 03:30

기본 언어로는 그렇게 할 수 없다고 생각합니다. 주석에서 언급했듯이 RankNTypes와 같은 몇 가지 확장을 활성화해야 할 수도 있습니다.

관련된 모든 유형이 숫자 유형이므로 (+1)과 같은 증분 함수를 다형성 함수로 사용하는 것이 좋습니다.

아래에서 시도해 보겠습니다 ghci.

$ ghci
GHCi, version 8.6.5: http://www.haskell.org/ghc/  :? for help
 λ> 
 λ> type TestT = Either Int Float 
 λ> 
 λ> :set +m
 λ> 
 λ> :set -XRankNTypes
 λ> :set -XScopedTypeVariables
 λ> 
 λ> {-
|λ> let { testM :: (forall a. Num a => a -> a) -> TestT -> TestT ;
|λ>       testM fn (Left x) = Left (fn x) ;
|λ>       testM fn (Right x) = Right (fn x) }
|λ> -}
 λ> 
 λ> :type testM
testM :: (forall a. Num a => a -> a) -> TestT -> TestT
 λ> 
 λ> testM (+3) (Left 42)
Left 45
 λ> 
 λ> testM (+3) (Right 3.14159)
Right 6.14159
 λ> 

참고 1 : 언어 확장을 생략하면 RankNTypes에 대한 힌트 메시지와 함께 중단됩니다.

2 주 : 당신이 사용하는 경우 forall a. Num a => (a -> a)대신 (forall a. Num a => a -> a), 그것은 또한 휴식.

참고 3 : 일부 선행 기술 : Alexis King의 유용한 의견이있는 SO-q38298119 .

2 DavidFox Dec 09 2020 at 10:26

이를 수행하는 한 가지 방법은 Bifunctor를 사용하는 것입니다.

Prelude> :m +Data.Bifunctor
Prelude Data.Bifunctor> bimap show show (Left 3)
Left "3"
Prelude Data.Bifunctor> bimap show show (Right 'x')
Right "'x'"
Prelude Data.Bifunctor>