[英]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
库来实现类似的代码并满足以下要求?
MyType
should be passed implicitly. MyType
值应该隐式传递。HasMyType
constraint is not specified, the default value of MyType
should be taken.HasMyType
约束,则应采用MyType
的默认值。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.