Hal*_*Ali 4 haskell types bifunctor
我通过阅读《第一原理》(Allen&Moronuki)的《Haskell编程》一书来学习Haskell 。
在有关Monad Transformers,Functor和Applicative组成部分的练习中,它要求读者为以下类型编写Bifunctor实例
data SemiDrei a b c = SemiDrei a
Run Code Online (Sandbox Code Playgroud)
我的第一次尝试(编译)是
instance Bifunctor (SemiDrei a) where
bimap f g (SemiDrei a) = SemiDrei a
Run Code Online (Sandbox Code Playgroud)
但是,看着它,在我看来我应该应该能够写,bimap f g = id因为最后一个参数不变或写了bimap f g x = x。两者都给我带来了编译错误,并且我希望有人可以向我解释为什么我不能bimap用这些较短的替代方法来表达,即为什么我必须指定(SemiDrei a)。
我在Haskell 8.6.5上运行了此命令(如果相关)
尝试:ID
instance Bifunctor (SemiDrei a) where
bimap f g = id
-- compile error message:
• Couldn't match type ‘a1’ with ‘b’
‘a1’ is a rigid type variable bound by
the type signature for:
bimap :: forall a1 b c d.
(a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
at src/Main.hs:69:5-9
‘b’ is a rigid type variable bound by
the type signature for:
bimap :: forall a1 b c d.
(a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
at src/Main.hs:69:5-9
Expected type: SemiDrei a a1 c -> SemiDrei a b d
Actual type: SemiDrei a b d -> SemiDrei a b d
• In the expression: id
In an equation for ‘bimap’: bimap f g = id
In the instance declaration for ‘Bifunctor (SemiDrei a)’
• Relevant bindings include
f :: a1 -> b (bound at src/Main.hs:69:11)
bimap :: (a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
(bound at src/Main.hs:69:5)
|
69 | bimap f g = id
| ^^
Run Code Online (Sandbox Code Playgroud)
尝试:fgx = x
instance Bifunctor (SemiDrei a) where
bimap f g x = x
-- compile error message:
• Couldn't match type ‘a1’ with ‘b’
‘a1’ is a rigid type variable bound by
the type signature for:
bimap :: forall a1 b c d.
(a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
at src/Main.hs:69:5-9
‘b’ is a rigid type variable bound by
the type signature for:
bimap :: forall a1 b c d.
(a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
at src/Main.hs:69:5-9
Expected type: SemiDrei a b d
Actual type: SemiDrei a a1 c
• In the expression: x
In an equation for ‘bimap’: bimap f g x = x
In the instance declaration for ‘Bifunctor (SemiDrei a)’
• Relevant bindings include
x :: SemiDrei a a1 c (bound at src/Main.hs:69:15)
f :: a1 -> b (bound at src/Main.hs:69:11)
bimap :: (a1 -> b) -> (c -> d) -> SemiDrei a a1 c -> SemiDrei a b d
(bound at src/Main.hs:69:5)
|
69 | bimap f g x = x
| ^
Run Code Online (Sandbox Code Playgroud)
实际上,最后一个参数并没有产生不变:其类型发生了变化。输入为SemiDrei a x y,输出为SemiDrei a p q,其中f :: x -> p和g :: y -> q。
这意味着您必须解构原始类型的值并重建新类型的值,这是您在原始实现中所做的。
但是您的直觉是正确的:这两个值确实具有相同的内存表示形式。GHC可以推断出这一事实,当它发生时,它将Coercible为您自动解决约束,这意味着您可以使用该coerce函数将一个转换为另一个:
bimap _ _ = coerce
Run Code Online (Sandbox Code Playgroud)
这在更简单的情况下显示了相同的问题:
data T a = K
foo :: T a -> T b
foo K = K -- type checks
bar :: T a -> T b
bar x = x -- type error
-- bar = id would also be a type error, for the same reason
Run Code Online (Sandbox Code Playgroud)
这里的问题是两个Kin foo值隐藏了它们的类型参数。更精确的定义是
-- pseudo code
foo (K @a) = K @b
Run Code Online (Sandbox Code Playgroud)
在这里您可以看到隐式类型参数发生了变化。当我们K在中定义时,GHC会自动为我们推断这些类型参数foo。由于它们是隐式的,因此它们看起来好像是相同K的,但是对于类型检查器而言则不是。
相反,当x在的定义中使用时bar,没有要推断的隐式类型参数。我们拥有了x :: T a,仅此而已。我们不能使用x并声称具有不同的类型T b。
最后,请注意,使用“安全强制”,我们可以执行直观上正确的种类id,将一种K(一种类型)转换为另一种类型K的另一种:
import Data.Coerce
baz :: T a -> T b
baz = coerce
Run Code Online (Sandbox Code Playgroud)
这是否更好是有争议的。在简单情况下,模式匹配比起来更容易理解coerce,因为后者可以执行各种各样的(安全)强制,可能使读者猜测类型级别实际发生的事情。
| 归档时间: |
|
| 查看次数: |
62 次 |
| 最近记录: |