简体   繁体   English

如何用“反射”包替换 ImplicitParams?

[英]How to replace ImplicitParams with the 'reflection' package?

I have an enumeration type, eg我有一个枚举类型,例如

data MyType = A | B

And I want to be able to pass values of this type implicitly to my functions.我希望能够将这种类型的值隐式传递给我的函数。 I can do this using the ImplicitParams GHC extension like this:我可以使用ImplicitParams GHC 扩展来做到这一点,如下所示:

type HasMyType = (?myType :: MyType)

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

But I've heard many times that it's better to use the Haskell package reflection for this task.但是我多次听说,最好使用 Haskell 包反射来完成这项任务。 Unfortunately, the reflection documentation doesn't explain how to write a similar code using the library.不幸的是, reflection文档没有解释如何使用库编写类似的代码。 And it's not that straightforward to figure it out.弄清楚它并不是那么简单。

So, my question is, whether it's possible to use the reflection library to implement a similar code and to satisfy the following requirements?那么,我的问题是,是否可以使用reflection库来实现类似的代码并满足以下要求?

  1. Values of MyType should be passed implicitly. MyType值应该隐式传递。
  2. If the HasMyType constraint is not specified, the default value of MyType should be taken.如果未指定HasMyType约束,则应采用MyType的默认值。
  3. It should be possible to override the value passed via the HasMyType constraint in a single place, eg at the beginning of the application.应该可以在一个地方覆盖通过HasMyType约束传递的值,例如在应用程序的开头。

Does something like this is possible?这样的事情可能吗? Or what would be the closest approximation of this using the reflection library?或者使用reflection库最接近的近似值是什么?

This answers two ways of implementing question 1. using reflection .这回答了实现问题1.使用反射的两种方法。

Using Reifies :使用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 can't abstract over type variables yet \\@name -> .. so it uses \\(Proxy :: Proxy name) -> .. . Haskell 还不能抽象类型变量\\@name -> ..所以它使用\\(Proxy :: Proxy name) -> ..

The Proxy can be removed from myFun where name is supplied with visible type applications, but reify still generates a Proxy whose name must be "extracted" Proxy可以从myFun中删除,其中name随可见类型应用程序提供,但reify仍然生成一个必须“提取”名称的Proxy

{-# 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"

A simpler option ( Given ) doesn't rely on type-level "names" to distinguish between different dictionaries, therefore it is more dangerous with the following warning:一个更简单的选项( Given )不依赖于类型级别的“名称”来区分不同的字典,因此以下警告更危险:

You should only give a single value for each type.您应该只为每种类型提供一个值。 If multiple instances are in scope, then the behavior is implementation defined.如果多个实例在范围内,则行为是实现定义的。

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"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM