一起使用makeLenses,类约束和类型同义词 [英] Using makeLenses, class constraints and type synonyms together

查看:72
本文介绍了一起使用makeLenses,类约束和类型同义词的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Haskell还是很陌生,想将Control.Lens中的makeLenses和类约束与类型同义词一起使用,以使我的函数类型更紧凑(可读?).

I'm quite new to Haskell and want to use makeLenses from Control.Lens and class constraints together with type synonyms to make my functions types more compact (readable?).

我试图提出一个最小的虚拟示例来演示我想要实现的目标,并且该示例除了此以外没有其他目的.

I've tried to come up with a minimal dummy example to demonstrate what I want to achieve and the example serves no other purpose than this.

在这篇文章的结尾,如果您对上下文感兴趣,我添加了一个与原始问题更接近的示例.

At the end of this post I've added an example closer to my original problem if you are interested in the context.

最小示例

例如,假设我定义了以下数据类型:

As an example, say I define the following data type:

data State a = State { _a :: a
                     } deriving Show     

,我也为此制造镜头:

makeLenses ''State

为了对类型构造函数State使用的类型参数a施加类约束,我使用了一个智能构造函数:

In order to enforce a class constraint on the type parameter a used by the type constructor State I use a smart constructor:

mkState :: (Num a) =>  a -> State a
mkState n = State {_a = n}

接下来,说我有许多具有类似类型签名的函数:

Next, say I have a number of functions with type signatures similar to this:

doStuff :: Num a => State a -> State a
doStuff s = s & a %~ (*2)

这一切都按预期工作,例如:

This all works as intended, for example:

test = doStuff . mkState $ 5.5 -- results in State {_a = 11.0}

问题

我尝试使用以下类型同义词:

I've tried to use the following type synonym:

type S = (Num n) => State n -- Requires the RankNTypes extensions

,以及:

{-# LANGUAGE RankNTypes #-}

,以简化doStuff的类型签名:

doStuff :: S -> S

,但是会出现以下错误:

, but this gives the following error:

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.

问题

我目前对Haskell的了解还不足以了解导致上述错误的原因.我希望有人能够解释导致错误的原因和/或建议其他方式来构造类型同义词,或者为什么这样的类型同义词是不可能的.

My current knowledge of Haskell is not sufficient to understand what causes the above error. I hope someone can explain what causes the error and/or suggest other ways to construct the type synonym or why such a type synonym is not possible.

背景

我原来的问题更接近于此:

My original problem looks closer to this:

data State r = State { _time :: Int
                     , _ready :: r
                     } deriving Show

makeLenses ''State

data Task = Task { ... }

在这里,我想使用以下智能构造函数将_ready的类型强制为Queue类的实例:

Here I want to enforce the type of _ready being an instance of the Queue class using the following smart constructor:

mkState :: (Queue q) => Int -> q Task -> State (q Task)
mkState t q  = State { _time = t
                     , _ready = q
                     }

我还有许多具有类型签名的函数,类似于:

I also have a number of functions with type signatures similar to this:

updateState :: Queue q => State (q Task) -> Task -> State (q Task)
updateState s x = s & ready %~ (enqueue x) & time %~ (+1)

我想使用类型同义词S来重写以下函数的类型:

I would like to use a type synonym S to be able to rewrite the type of such functions as:

updateState :: S -> Task -> S

,但是与第一个最小示例一样,我不知道如何定义类型同义词S或是否完全可能.

, but as with the first minimal example I don't know how to define the type synonym S or whether it is possible at all.

尝试简化类型签名也许没有真正的好处?

Maybe there is no real benefit in trying to simplify the type signatures?

相关阅读

我已经阅读了以下有关SO的问题:

I've read the following related questions on SO:

  • Class constraints for data records
  • Are type synonyms with typeclass constraints possible?

这也可能是相关的,但是鉴于我目前对Haskell的了解,我无法真正理解所有这些内容:

This might also be related but given my current understanding of Haskell I cannot really understand all of it:

跟进

自从我有机会做一些Haskell以来已经有一段时间了.感谢@bheklilr,我现在设法引入了一个类型同义词,只是遇到了我仍然无法理解的下一个类型错误.我发布了以下跟进问题类型同义词导致类型错误关于新输入错误.

It's been a while since I've had the opportunity to do some Haskell. Thanks to @bheklilr I've now managed to introduce a type synonym only to hit the next type error I'm still not able to understand. I've posted the following follow-up question Type synonym causes type error regarding the new type error.

推荐答案

特别是由于.运算符和对RankNTypes的组合使用,您会看到该错误.如果您将其更改为

You see that error in particular because of the combination of the . operator and your use of RankNTypes. If you change it from

test = doStuff . mkState $ 5.5

test = doStuff $ mkState 5.5

甚至

test = doStuff (mkState 5.5)

它将编译.为什么是这样?查看类型:

it will compile. Why is this? Look at the types:

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)

希望括号能在这里使您清楚,doStuffforall n. Num n ...nmkStateNum n => ...的不同类型变量,因为forall的范围仅扩展到括号的结尾.因此,这些函数实际上无法组合,因为编译器将它们视为单独的类型!出于这个原因,实际上对于$运算符有一些特殊的规则,专门用于使用ST monad,这样您就可以执行runST $ do ....

Hopefully the parentheses help make it clear here, the n from forall n. Num n ... for doStuff is a different type variable from the Num n => ... for mkState because the scope of the forall only extends to the end of the parentheses. So these functions can't actually compose because the compiler sees them as separate types! There are actually special rules for the $ operator specifically for using the ST monad precisely for this reason, just so you can do runST $ do ....

您可以使用GADT轻松完成所需的工作,但我不相信lens'TemplateHaskell将适用于GADT类型.但是,在这种情况下,您可以轻松编写自己的脚本,因此没什么大不了的.

You may be able to accomplish what you want easier using GADTs, but I don't believe lens' TemplateHaskell will work with GADT types. However, you can write your own pretty easily in this case, so it isn't that big of a deal.

进一步的解释:

doStuff . mkState $ 5.5

doStuff $ mkState 5.5

在第一个中,doStuff表示对于所有 Num类型n,其类型为State n -> State n,而mkState表示对于某些 Num类型m,其类型为m -> State m.这两种类型由于针对所有人"和针对某些人"的量化而不同(因此ExistentialQuantification),因为将它们组合将意味着对于某些Num m您可以产生所有Num n.

In the first one, doStuff says that for all Num types n, its type is State n -> State n, whereas mkState says for some Num type m, its type is m -> State m. These two types are not the same because of the "for all" and "for some" quantifications (hence ExistentialQuantification), since composing them would mean that for some Num m you can produce all Num n.

doStuff $ mkState 5.5中,您具有等价于

(forall n. Num n => State n -> State n) $ (Num m => State m)

请注意,由于完全应用了mkState 5.5,因此$之后的类型不是函数.这样做之所以有效,是因为对于所有 Num n,您都可以执行State n -> State n,并且为其提供了一些Num m => State m.这很直观.同样,这里的区别是组成与应用.您不能将适用于某些类型的函数与适用于所有类型的函数组合在一起,但是可以将值传递给适用于所有类型的函数(此处的所有类型"表示forall n. Num n => n).

Notice that the type after the $ is not a function because mkState 5.5 is fully applied. So this works because for all Num n you can do State n -> State n, and you're providing it some Num m => State m. This works intuitively. Again, the difference here is the composition versus application. You can't compose a function that works on some types with a function that works on all types, but you can pass a value to a function that works on all types ("all types" here meaning forall n. Num n => n).

这篇关于一起使用makeLenses,类约束和类型同义词的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆