使用“Prism”等光学类型时无法在“DerivingVia”期间进行强制转换
Unable to coerce during `DerivingVia` when using optics types like `Prism'`
我无法在使用 optics-core
类型的任何类型上使用 DerivingVia
自动派生实例,例如 Prism'
;我从编译器得到的错误是:
src/Main.hs:24:13-19: error:
• Couldn't match type ‘Foo’ with ‘Stringable Foo’
arising from the coercion of the method ‘mkPrism’
from type ‘MyPrism (Stringable Foo)’ to type ‘MyPrism Foo’
• When deriving the instance for (MkPrism Foo)
|
24 | deriving (MkPrism) via (Stringable Foo)
| ^^^^^^^
有问题的类型是:
newtype MyPrism a
= MyPrism (Prism' String a)
如果我用它的底层表示替换 Prism'
类型,编译成功,即:
newtype MyPrism a
= MyPrism (a -> String, String -> Maybe a)
- 为什么我在使用
Prism'
时会看到强制转换问题?是不是因为the Optic
constructor不是包导出的?
- 通常的解决方法是什么?
你可以在这里看到完整的重现程序:https://github.com/srid/q-optics-coercion/blob/f1e3df5e1c5c0e4a51eacb6439fb8c627e4c7bd5/src/Main.hs ...如果你安装了 Nix,你可以在这个 repo 中 运行 bin/run
。
编辑:一旦我import Optics.Internal.Optic
,错误信息就变成:
src/Main.hs:25:13-19: error:
• Couldn't match representation of type ‘p i Foo Foo’
with that of ‘p i (Stringable Foo) (Stringable Foo)’
arising from the coercion of the method ‘mkPrism’
from type ‘MyPrism (Stringable Foo)’ to type ‘MyPrism Foo’
• When deriving the instance for (MkPrism Foo)
|
25 | deriving (MkPrism) via (Stringable Foo)
|
一般来说,profunctor 光学器件(例如 lens
和 optics
包中使用的光学器件)不能直接强制转换。粗略地说,棱镜的内部表示是这样的:
newtype Prism' s a
= Prism' (forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s))
(好吧,至少 Control.Lens
是这样。Optics.Core
可能会稍微复杂一些。)
这里的问题是 a
和 s
作为参数出现在这种类型的 higher-kinded 类型变量(f
和 p
)中。因此,他们的“角色”是名义上的(请参阅 GHC 手册中的 Role Inference 部分以了解原因),并且这些名义上的角色可以防止直接强制。
幸运的是,Optics.Coerce
中有一些函数是专门用来处理这个问题的。您将无法直接通过 newtype 派生出 MyPrism
类型,但您应该能够编写:
instance MkPrism Foo where
mkPrism = MyPrism . coerceA . coerceB $ p
where MyPrism p = mkPrism :: MyPrism (Stringable Text)
我无法在使用 optics-core
类型的任何类型上使用 DerivingVia
自动派生实例,例如 Prism'
;我从编译器得到的错误是:
src/Main.hs:24:13-19: error:
• Couldn't match type ‘Foo’ with ‘Stringable Foo’
arising from the coercion of the method ‘mkPrism’
from type ‘MyPrism (Stringable Foo)’ to type ‘MyPrism Foo’
• When deriving the instance for (MkPrism Foo)
|
24 | deriving (MkPrism) via (Stringable Foo)
| ^^^^^^^
有问题的类型是:
newtype MyPrism a
= MyPrism (Prism' String a)
如果我用它的底层表示替换 Prism'
类型,编译成功,即:
newtype MyPrism a
= MyPrism (a -> String, String -> Maybe a)
- 为什么我在使用
Prism'
时会看到强制转换问题?是不是因为theOptic
constructor不是包导出的? - 通常的解决方法是什么?
你可以在这里看到完整的重现程序:https://github.com/srid/q-optics-coercion/blob/f1e3df5e1c5c0e4a51eacb6439fb8c627e4c7bd5/src/Main.hs ...如果你安装了 Nix,你可以在这个 repo 中 运行 bin/run
。
编辑:一旦我import Optics.Internal.Optic
,错误信息就变成:
src/Main.hs:25:13-19: error:
• Couldn't match representation of type ‘p i Foo Foo’
with that of ‘p i (Stringable Foo) (Stringable Foo)’
arising from the coercion of the method ‘mkPrism’
from type ‘MyPrism (Stringable Foo)’ to type ‘MyPrism Foo’
• When deriving the instance for (MkPrism Foo)
|
25 | deriving (MkPrism) via (Stringable Foo)
|
一般来说,profunctor 光学器件(例如 lens
和 optics
包中使用的光学器件)不能直接强制转换。粗略地说,棱镜的内部表示是这样的:
newtype Prism' s a
= Prism' (forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s))
(好吧,至少 Control.Lens
是这样。Optics.Core
可能会稍微复杂一些。)
这里的问题是 a
和 s
作为参数出现在这种类型的 higher-kinded 类型变量(f
和 p
)中。因此,他们的“角色”是名义上的(请参阅 GHC 手册中的 Role Inference 部分以了解原因),并且这些名义上的角色可以防止直接强制。
幸运的是,Optics.Coerce
中有一些函数是专门用来处理这个问题的。您将无法直接通过 newtype 派生出 MyPrism
类型,但您应该能够编写:
instance MkPrism Foo where
mkPrism = MyPrism . coerceA . coerceB $ p
where MyPrism p = mkPrism :: MyPrism (Stringable Text)