8

我有一个枚举类型,例如

data MyType = A | B

而且我希望能够将这种类型的值隐式传递给我的函数。我可以ImplicitParams像这样使用 GHC 扩展来做到这一点:

type HasMyType = (?myType :: MyType)

myFun :: HasMyType => String
myFun = case ?myType of
    A -> "Foo"
    B -> "Bar"

但我多次听说最好使用 Haskell 包反射来完成这项任务。不幸的是,reflection文档没有解释如何使用该库编写类似的代码。而且要弄清楚它并不是那么简单。

所以,我的问题是,是否可以使用该reflection库来实现类似的代码并满足以下要求?

  1. 的值MyType应该隐式传递。
  2. 如果HasMyType未指定约束,MyType则应采用默认值。
  3. 应该可以HasMyType在一个地方覆盖通过约束传递的值,例如在应用程序的开头。

这样的事情可能吗?或者使用reflection图书馆最接近的近似值是什么?

4

1 回答 1

3

这回答了实现问题1.使用反射的两种方法。

使用Reifies

type HasMyType :: forall k. k -> Constraint
type HasMyType name = Reifies name MyType

myFun :: HasMyType name => Proxy name -> String
myFun name = case reflect name of
  A -> "Foo"
  B -> "Bar"

-- reify :: MyType -> (forall name. HasMyType name => Proxy name -> res) -> res
>> reify A myFun
"Foo"
>> reify B myFun
"Bar"
>> reify A \name -> myFun name
"Foo"
>> reify B \name -> myFun name
"Bar"

Haskell 还不能抽象类型变量,\@name -> ..所以它使用\(Proxy :: Proxy name) -> ...

Proxy可以从提供可见类型应用程序的地方删除,但仍会myFun生成名称必须“提取”的namereifyProxy

{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications #-} ..

myFun :: forall name. HasMyType name => String
myFun = case reflect @name Proxy of
  A -> "Foo"
  B -> "Bar"

>> reify A \(_ :: _ name) -> myFun @name
"Foo"
>> reify B \(_ :: _ name) -> myFun @name
"Bar"

一个更简单的选项 ( Given) 不依赖于类型级别的“名称”来区分不同的字典,因此使用以下警告会更加危险:

您应该只为每种类型提供一个值。如果多个实例在范围内,则行为由实现定义。

type HasMyType :: Constraint
type HasMyType = Given MyType

myFun :: HasMyType => String
myFun = case given of
  A -> "Foo"
  B -> "Bar"

-- give :: MyType -> (HasMyType => res) -> res
>> give A myFun
"Foo"
>> give B myFun
"Bar"
于 2020-09-02T11:53:20.503 回答