如何将多态函数应用于 Either 的两边?
How to apply a polymorphic function to both sides of an Either?
我试过这个:
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)
我认为您无法用基础语言做到这一点。如评论中所述,您可能需要启用几个扩展,例如 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 的有用评论。
一种方法是使用 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>
我试过这个:
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)
我认为您无法用基础语言做到这一点。如评论中所述,您可能需要启用几个扩展,例如 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: 这里有一些现有技术:
一种方法是使用 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>