我一直在尝试编写一个自定义的Optics数据结构,它可以泛化Lenses、Prisms和Traversals。我的数据结构长这样:
data Optic m a b = Optic { view :: a -> m b
, over :: a -> (b -> b) -> a
}
我想编写一个函数来组合两个“Optic”,
optic1 :: Optic m a b
和 optic2 :: Optic n b c
,以生成包含 view :: a -> m (n c)
和 over :: a -> (c -> c) -> a
的“Optic”。在我的脑海中,这个组合的“Optic”的类型应该是 Optic (m n) a c
,但实际上这行不通——GHC 会抱怨 m 有一个过多的类型参数,而 n 则缺少一个。以下是我未能编译的 compose 函数实现代码:compose :: Optic m a b -> Optic n b c -> (m b -> (b -> n c) -> m (n c)) -> Optic (m n) a c
compose optic1 optic2 glue = Optic { view = viewCompose (view optic1) (view optic2) glue
, over = overCompose (over optic1) (over optic2)
}
viewCompose :: (a -> m b) -> (b -> n c) -> (m b -> (b -> n c) -> m (n c)) -> a -> m (n c)
viewCompose view1 view2 glue x = glue (view1 x) view2
overCompose :: (a -> (b -> b) -> a) -> (b -> (c -> c) -> b) -> a -> (c -> c) -> a
overCompose over1 over2 x f = over1 x (\y -> over2 y f)
GHC的错误信息如下:
optic.hs:7:83: error:
• Expecting one fewer argument to ‘m n’
Expected kind ‘* -> *’, but ‘m n’ has kind ‘*’
• In the first argument of ‘Optic’, namely ‘m n’
In the type signature:
compose :: Optic m a b
-> Optic n b c -> (m b -> (b -> n c) -> m (n c)) -> Optic (m n) a c
optic.hs:7:85: error:
• Expecting one more argument to ‘n’
Expected a type, but ‘n’ has kind ‘* -> *’
• In the first argument of ‘m’, namely ‘n’
In the first argument of ‘Optic’, namely ‘m n’
In the type signature:
compose :: Optic m a b
-> Optic n b c -> (m b -> (b -> n c) -> m (n c)) -> Optic (m n) a c
如果我创建了类型为
Optic Maybe Int Int
的光学对象,那么GHC会理解第一个类型参数具有* -> *
的类型,并且不会因为参数不足而抱怨。但是我不知道如何将类型组合在一起,以创建另一种* -> *
类型。是否有任何方法(带或不带语言扩展)可以表达类似以下内容的东西:
Optic (forall t. m (n t)) a c
Compose
,它是否有效?也就是说,Optic (Compose m n) a c
。 - bradrnCompose
会使得view :: a -> Compose m n c
,而不是 OP 所需的view :: a -> m (n c)
。 - Fyodor SoikinCompose
是最接近的选择。Haskell不允许类型级别的lambda,因为在类型推断的许多点上它假定单射性,这是由于我们有_类型名称_而不是_类型函数_,粗略地说(也请参见名义与结构类型)。 - chi