Haskell 使用;“限制”;使包装函数不那么多态的包

Haskell 使用;“限制”;使包装函数不那么多态的包,haskell,polymorphism,typeclass,Haskell,Polymorphism,Typeclass,我有以下数据类型: {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneKindSignatures #-} {-# LANGUAGE TypeApplications #

我有以下数据类型:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}

import Data.Proxy
import Data.Constraint
import Data.Kind

type Foo :: (Type -> Constraint) -> Type
data Foo c = Foo (Proxy c) (forall x. c x => x -> x)
它看起来不是很有用,但它是有用的数据类型的简化版本

我想使用包编写约束上的映射函数,使用以下证据:

其思想是,如果
more
约束比
less
约束更强,则应该可以将
Foo
中约束较少的函数转换为约束较多的函数

我认为所有v的
都在正确的位置。如果它覆盖所有签名,则调用方将是选择
v
,这在使用多态函数时似乎适得其反

这种天真的实施尝试不起作用:

restrictFoo :: forall less more. (forall v. more v :- less v) -> Foo less -> Foo more 
restrictFoo (Sub Dict) (Foo proxy f) = 
     Foo (Proxy @more) f
它失败了

* Could not deduce: more v0 arising from a pattern
* In the pattern: Dict
  In the pattern: Sub Dict
...
* Could not deduce: less x arising from a use of `f'
  from the context: less v0

如何正确编写
restrictFoo
的正文?

您对
Dict
的模式匹配过于急切
Dict
提供了约束
less v
,但它来自
Sub
,只有当您提供
更多v
时,才会提供
less v
,而此时您还没有;您甚至没有
v
,因此专门化
:-
参数也为时过早

Foo
的第二个字段中,您只得到一个约束
more x
,因此在那里的
Sub
上进行模式匹配

restrictFoo :: forall less more. (forall v. more v :- less v) -> Foo less -> Foo more 
restrictFoo sub (Foo proxy f) = Foo (Proxy @more) g where
  g :: forall x. more x => x -> x
  g = case sub @x of
    Sub Dict -> f

问题是,当您展开
子目录
时,字典将要应用的范围中需要有一些
v
,但没有。因此,编译器试图创建一个新的var
v0
,希望最终将其与某个东西统一起来,但最终不会出现这种情况

但是让我们思考一下:那
v
最终从何而来?那么,展开
子目录
会给我们带来什么?它给我们一个
更少的v
(对于一些未知的
v
),只要我们能给它
更多的v
。在什么地方我们碰巧有了更多的v,而需要更少的v?当然,为什么它在
Foo
的第二个参数中

这意味着只有在实际调用
Foo
的第二个参数时,我们才需要展开
Sub Dict
。在这一点上,调用它的人将提供一个
more v
的实例,因此我们可以将它交给
Sub Dict
,并得到一个
less v

看起来很幼稚,就像这样:

restrictFoo e (Foo proxy f) =
     Foo (Proxy @more) \x -> case e of Sub Dict -> f x
但这仍然不起作用:编译器不知道
x
的类型与它需要用于
子目录的类型相同。这两者之间没有联系

要创建这样的连接,我们需要显式地量化
x
的类型,为此,我们需要给新函数一个类型签名:

restrictFoo e (Foo proxy f) =
     Foo (Proxy @more) f'
     where
        f' :: forall x. more x => x -> x
        f' x = case e of Sub (Dict :: Dict (less x)) -> f x
现在这就行了:当有人从
Foo
中取出
f'
并调用它时,他们必须提供一个
more x
的实例,并且将该实例放在范围内将允许我们将其交给
Sub
,并得到一个
Dict(less x)
,这将给我们一个
less x
的实例,这最终允许我们调用原始的
f

restrictFoo e (Foo proxy f) =
     Foo (Proxy @more) f'
     where
        f' :: forall x. more x => x -> x
        f' x = case e of Sub (Dict :: Dict (less x)) -> f x