为什么“强制”不隐式地将 Compose 应用于这些函数?

ama*_*loy 8 haskell applicative

考虑这种类型:

\n
data Vec3 = Vec3 {_x, _y, _z :: Int}\n
Run Code Online (Sandbox Code Playgroud)\n

我有一些函数都采用相同的输入,并且可能无法计算字段:

\n
data Input\ngetX, getY, getZ :: Input -> Maybe Int\n
Run Code Online (Sandbox Code Playgroud)\n

我可以编写一个函数来尝试所有这三个字段构造函数:

\n
v3 :: Input -> Maybe Vec3\nv3 i = liftA3 Vec3 (getX i) (getY i) (getZ i)\n
Run Code Online (Sandbox Code Playgroud)\n

但必须将输入传递i三次左右,这有点烦人。函数本身就是一个应用函子,因此可以替换foo x = bar (f x) (g x) (h x)foo = liftA3 bar f g h

\n

在这里,我的barliftA3 Vec3,所以我可以写

\n
v3' :: Input -> Maybe Vec3\nv3' = liftA3 (liftA3 Vec3) getX getY getZ\n
Run Code Online (Sandbox Code Playgroud)\n

但这有点恶心,当我们以这种方式(((->) Input)Maybe)使用组合应用程序时,就会有Compose新类型来处理这种事情。有了它我就可以写

\n
v3'' :: Input -> Maybe Vec3\nv3'' = getCompose go\n  where go = liftA3 Vec3 x y z\n        x = Compose getX\n        y = Compose getY\n        z = Compose getZ\n
Run Code Online (Sandbox Code Playgroud)\n

好吧,这并不是一个很好的字符节省,但我们现在使用一个组合函子而不是两个,这很好。我想我可以用coerce它来赢回一些角色:毕竟,x它只是一个新类型的包装器getX,对于其他字段也是如此。所以我想我可以强迫liftA3接受三个Input -> Maybe Vec3而不是接受三个Compose ((->) Input) Maybe Vec3

\n
v3''' :: Input -> Maybe Vec3\nv3''' = getCompose go\n  where go = coerce liftA3 Vec3 getX getY getZ\n
Run Code Online (Sandbox Code Playgroud)\n

但这不起作用,会产生错误消息:

\n
tmp.hs:23:14: error:\n    \xe2\x80\xa2 Couldn't match representation of type \xe2\x80\x98f0 c0\xe2\x80\x99\n                               with that of \xe2\x80\x98Input -> Maybe Int\xe2\x80\x99\n        arising from a use of \xe2\x80\x98coerce\xe2\x80\x99\n    \xe2\x80\xa2 In the expression: coerce liftA3 Vec3 getX getY getZ\n      In an equation for \xe2\x80\x98go\xe2\x80\x99: go = coerce liftA3 Vec3 getX getY getZ\n      In an equation for \xe2\x80\x98v3'''\xe2\x80\x99:\n          v3'''\n            = getCompose go\n            where\n                go = coerce liftA3 Vec3 getX getY getZ\n   |\n23 |   where go = coerce liftA3 Vec3 getX getY getZ\n   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n
Run Code Online (Sandbox Code Playgroud)\n

我不明白为什么不。我可以写coerce getX :: Compose ((->) Input) Maybe Int,这很好。通常,可以强制函数以强制其参数或返回类型,如

\n
coerce ((+) :: Int -> Int -> Int) (Max (5::Int)) (Min (8::Int)) :: Sum Int\n
Run Code Online (Sandbox Code Playgroud)\n

事实上我可以写出所有coerce单独写出所有的 s :

\n
v3'''' :: Input -> Maybe Vec3\nv3'''' = getCompose go\n  where go = liftA3 Vec3 x y z\n        x = coerce getX\n        y = coerce getY\n        z = coerce getZ\n
Run Code Online (Sandbox Code Playgroud)\n

那么为什么不能liftA3强迫自己接受getX而不是coerce getX让我使用v3'''呢?

\n

Li-*_*Xia 5

如果您向 提供适用函子liftA3,则进行以下类型检查:

v3' :: Input -> Maybe Vec3
v3' = coerce (liftA3 @(Compose ((->) Input) Maybe) Vec3) getX getY getZ
Run Code Online (Sandbox Code Playgroud)

coerce liftA3没有任何注释的情况下,无法推断要使用什么应用函子liftA3。这些都没有提到类型Compose。它也可能是ReaderT Input MaybeKleisli Maybe Input一种具有非法实例或更奇特的类型。

getCompose (coerce liftA3 _ _ _)(您的最后一次尝试)中,getCompose不约束liftA3( 的“内部” coerce),因为getCompose是 的“外部” coerce。它要求 的结果类型liftA3可强制为Compose ((->) Input) Maybe Vec3,但它可能仍然不等于 。

  • @amalloy 在“liftA3”中,有多个“f”被限制为彼此相等,因此信息可以从“输出”“f”流向输入。但是 `coerce liftA3` 并不共享该属性 - 也许您想要选择不同的 `newtype` 而不是 `Compose`! (2认同)