Bagaimana cara menerapkan fungsi polimorfik ke kedua sisi Either?

Dec 06 2020

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

3 jpmarinier Dec 06 2020 at 03:30

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.

2 DavidFox Dec 09 2020 at 10:26

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>