我对Haskell很新,并希望将makeLensesfrom Control.Lens和class约束与类型同义词一起使用,以使我的函数类型更紧凑(可读?).
我试图想出一个最小的虚拟示例来演示我想要实现的内容,并且该示例除此之外没有任何其他用途.
在这篇文章的最后,如果您对上下文感兴趣,我已经添加了一个更接近原始问题的示例.
最小的例子
举个例子,假设我定义了以下数据类型:
data State a = State { _a :: a
} deriving Show
Run Code Online (Sandbox Code Playgroud)
,我也制作镜片:
makeLenses ''State
Run Code Online (Sandbox Code Playgroud)
为了对a类型构造函数State使用的类型参数强制执行类约束,我使用了一个智能构造函数:
mkState :: (Num a) => a -> State a
mkState n = State {_a = n}
Run Code Online (Sandbox Code Playgroud)
接下来,假设我有许多类型签名的函数,类似于:
doStuff :: Num a => State a -> State a
doStuff s = s & a %~ (*2)
Run Code Online (Sandbox Code Playgroud)
这一切都按预期工作,例如:
test = doStuff . mkState $ 5.5 -- results in State {_a = 11.0}
Run Code Online (Sandbox Code Playgroud)
问题
我试过使用以下类型的同义词:
type S = (Num n) => State n -- Requires the RankNTypes extensions
Run Code Online (Sandbox Code Playgroud)
, 和...一起:
{-# LANGUAGE RankNTypes #-}
Run Code Online (Sandbox Code Playgroud)
,试图简化类型签名doStuff:
doStuff :: S -> S
Run Code Online (Sandbox Code Playgroud)
,但这会产生以下错误:
Couldn't match type `State a0' with `forall n. Num n => State n'
Expected type: a0 -> S
Actual type: a0 -> State a0
In the second argument of `(.)', namely `mkState'
In the expression: doStuff . mkState
In the expression: doStuff . mkState $ 5.5
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)
题
我目前对Haskell的了解不足以理解导致上述错误的原因.我希望有人可以解释导致错误的原因和/或建议构建类型同义词的其他方法,或者为什么不能使用这种类型的同义词.
背景
我原来的问题看起来更接近这个:
data State r = State { _time :: Int
, _ready :: r
} deriving Show
makeLenses ''State
data Task = Task { ... }
Run Code Online (Sandbox Code Playgroud)
在这里,我想使用以下智能构造函数强制执行类_ready的实例类型Queue:
mkState :: (Queue q) => Int -> q Task -> State (q Task)
mkState t q = State { _time = t
, _ready = q
}
Run Code Online (Sandbox Code Playgroud)
我还有许多类型签名的函数,类似于:
updateState :: Queue q => State (q Task) -> Task -> State (q Task)
updateState s x = s & ready %~ (enqueue x) & time %~ (+1)
Run Code Online (Sandbox Code Playgroud)
我想使用类型同义词S来重写这些函数的类型:
updateState :: S -> Task -> S
Run Code Online (Sandbox Code Playgroud)
,但与第一个最小的例子一样,我不知道如何定义类型同义词S或者它是否可能.
也许在尝试简化类型签名方面没有真正的好处?
相关阅读
我在SO上阅读了以下相关问题:
这可能也是相关的,但鉴于我目前对Haskell的理解,我无法真正理解所有这些:
跟进
已经有一段时间了,因为我有机会做一些Haskell.感谢@bheklilr我现在设法引入了一个类型同义词,只是为了找到我仍然无法理解的下一个类型错误.我发布了以下后续问题类型同义词导致关于新类型错误的类型错误.
您会看到该错误,尤其是由于.运算符和您的使用的组合RankNTypes。如果你把它从
test = doStuff . mkState $ 5.5
Run Code Online (Sandbox Code Playgroud)
到
test = doStuff $ mkState 5.5
Run Code Online (Sandbox Code Playgroud)
甚至
test = doStuff (mkState 5.5)
Run Code Online (Sandbox Code Playgroud)
它会编译。为什么是这样?看类型:
doStuff :: forall n. Num n => State n -> State n
mkState :: Num n => n -> State n
(doStuff) . (mkState) <===> (forall n. Num n => State n -> State n) . (Num n => n -> State n)
Run Code Online (Sandbox Code Playgroud)
希望括号有助于在这里澄清,nfrom forall n. Num n ...fordoStuff是与Num n => ...for不同的类型变量mkState,因为 only 的范围forall延伸到括号的末尾。因此,这些函数实际上无法组合,因为编译器将它们视为单独的类型!$实际上,出于这个原因,对于专门使用 monad 的操作符来说,有一些特殊的规则ST,所以你可以这样做runST $ do ...。
您也许可以使用 GADT 更轻松地完成您想要的任务,但我不相信lens'TemplateHaskell可以与 GADT 类型一起使用。然而,在这种情况下,您可以很容易地编写自己的代码,所以这没什么大不了的。
进一步解释:
doStuff . mkState $ 5.5
Run Code Online (Sandbox Code Playgroud)
与
doStuff $ mkState 5.5
Run Code Online (Sandbox Code Playgroud)
在第一个中,doStuff表示对于所有 Num类型n,其类型是State n -> State n,而mkState表示对于某些 Num类型m,其类型是m -> State m。这两种类型并不相同,因为“对于所有”和“对于某些”量化(因此ExistentialQuantification),因为组合它们意味着对于某些Num m您可以生成所有Num n。
在 中doStuff $ mkState 5.5,您相当于
(forall n. Num n => State n -> State n) $ (Num m => State m)
Run Code Online (Sandbox Code Playgroud)
请注意, 后面的类型$不是函数,因为mkState 5.5已完全应用。所以这是有效的,因为你尽一切 Num n努力State n -> State n,并且你正在提供一些Num m => State m。这很直观。同样,这里的区别在于组合与应用。您无法将适用于某些类型的函数与适用于所有类型的函数组合在一起,但您可以将值传递给适用于所有类型的函数(“所有类型”此处的意思是forall n. Num n => n)。
| 归档时间: |
|
| 查看次数: |
228 次 |
| 最近记录: |