使用“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)

你可以在这里看到完整的重现程序: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 光学器件(例如 lensoptics 包中使用的光学器件)不能直接强制转换。粗略地说,棱镜的内部表示是这样的:

newtype Prism' s a 
  = Prism' (forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s))

(好吧,至少 Control.Lens 是这样。Optics.Core 可能会稍微复杂一些。)

这里的问题是 as 作为参数出现在这种类型的 higher-kinded 类型变量(fp)中。因此,他们的“角色”是名义上的(请参阅 GHC 手册中的 Role Inference 部分以了解原因),并且这些名义上的角色可以防止直接强制。

幸运的是,Optics.Coerce中有一些函数是专门用来处理这个问题的。您将无法直接通过 newtype 派生出 MyPrism 类型,但您应该能够编写:

instance MkPrism Foo where
  mkPrism = MyPrism . coerceA . coerceB $ p
    where MyPrism p = mkPrism :: MyPrism (Stringable Text)