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
,但没有。因此,编译器试图创建一个新的varv0
,希望最终将其与某个东西统一起来,但最终不会出现这种情况
但是让我们思考一下:那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