Bagaimana cara menerapkan fungsi polimorfik ke kedua sisi Either?
Saya mencoba ini:
type TestT = Either Int Float
testM :: (a -> a) -> TestT -> TestT
testM f (Left x) = Left (f x)
testM f (Right x) = Right (f x)
tetapi tidak berhasil, adakah cara untuk melakukan ini? Saya melihat-lihat dan semua yang serupa benar-benar rumit dan terbatas.
Pesan kesalahan, seperti yang diminta:
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)
Jawaban
Saya tidak berpikir Anda bisa melakukan itu dalam bahasa dasar. Seperti yang disebutkan di komentar, Anda mungkin perlu mengaktifkan beberapa ekstensi, seperti RankNTypes.
Karena semua tipe yang terlibat adalah numerik, sangat menggoda untuk menggunakan fungsi increment, seperti (+1) sebagai fungsi polimorfik.
Mari kita coba di bawah 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
λ>
Catatan 1: Jika Anda menghilangkan ekstensi bahasa, itu rusak, dengan pesan yang mengisyaratkan RankNTypes.
Catatan 2: Jika Anda menggunakan, forall a. Num a => (a -> a)
bukan (forall a. Num a => a -> a)
, itu juga rusak.
Catatan 3: Beberapa karya seni sebelumnya di sini: SO-q38298119 dengan komentar berguna dari Alexis King.
Salah satu cara untuk melakukannya adalah dengan 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>