这回答了实现问题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
生成名称必须“提取”的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"
一个更简单的选项 ( 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"