我可以使用DataKinds编写一个返回参数编码类型的值的函数吗?

拉米丝·贾亚提耶卡(Ramith Jayatilleka)

假设我有一种货币类型:

data Currency = USD | EUR | YEN

和一个存储int的Money类型,并由给定的Currency参数化(通过DataKinds扩展,货币升为一种)。

data Money :: Currency -> * where
    Money :: Int -> Money c

是否可以编写将moneyOf货币值作为参数并返回由货币值的相应类型参数化的货币值的函数?例如moneyOf :: Currency -> Money c,但是我们得到了一个编译时保证,c即从Currency值生成的类型是什么?

Antal Spector-Zabusky

否,但是有解决方法。如您所见,您需要编写的类型类似于moneyOf :: (c :: Currency) -> Int -> Money c,在c类型函数实现本身(moneyOf _ amt = Money amt都受约束这不是我们在Haskell中可以做的事情。那我们该怎么办呢?有两种选择,具体取决于您的实际需求。

选项1:代理。定义多类型

data Proxy (t :: k) = Proxy

这种类型背后的想法是,您可以使用Proxy :: Proxy t一种绕过类型t的术语化表示形式的方式。因此,例如,我们可以定义:

moneyOf :: Proxy c -> Int -> Money c
moneyOf _ = Money

然后,我们可以称其moneyOf (Proxy :: Proxy USD) 10为get Money 10 :: Money USD您可以使用的技巧是改用给函数指定类型proxy k -> Int -> Money c(请注意小写proxy!),这样proxy可以与任意函数类型统一。¹这对于将参数传递给函数以修复其返回类型非常有用,但实际上并没有。真的不让您做任何其他事情。

正如您所描述的那样,我认为代理可能最适合解决它。(假设普通的类型签名(例如)Money 10 :: Money USD不起作用,也就是说,当您可以使用它们时,它们甚至更简单!)

选项2:单例类型。但是,如果发现您需要更多的通用性(或者您只是好奇),那么另一种方法是创建一个单例类型,如下所示:

data SingCurrency (c :: Currency) where
  SUSD :: SingCurrency USD
  SEUR :: SingCurrency EUR
  SYEN :: SingCurrency YEN

之所以称为“单一类型”,是因为每个SingCurrency c成员只有一个成员(例如SUSD是type的唯一值SingCurrency USD)。现在,您可以写

moneyOf :: SingCurrency c -> Int -> Money c
moneyOf _ = Money

在这里,moneyOf SUSD 10计算为Money 10 :: Money USD但是,仅此一项就不会为您带来任何好处(除了打字少了一点)。当您想要生产单例时,它们会特别有趣:

class SingCurrencyI (c :: Currency) where
  sing :: SingCurrency c

instance SingCurrencyI USD where scur = SUSD
instance SingCurrencyI EUR where scur = SEUR
instance SingCurrencyI YEN where scur = SYEN

现在,如果有SingCurrencyI c约束,则可以使用来自动生成相应的SingCurrency csing,从而使您可以从类型级别移至术语级别。(请注意,尽管Currencys都是的实例SingCurrencyI,但如果需要,则需要显式指定约束。²)我想不出有什么好的例子来使用它。我认为我的建议是仅在发现自己无法完成所需的情况时使用单例,并且意识到单例的额外类型值同步将对您有帮助(并且在您无法做到的情况下)根据情况重新设计自己)。

如果你发现自己使用的单身,机械是所有设置为你singletons,在更一般的情况:有一个数据系列Sing :: k -> *是需要的地方SingCurrency; 并且有一个类型类SingI :: k -> Constraint可以代替SingCurrencyI,它具有单个member sing :: SingI a => Sing a还有一个功能withSingI :: Sing n -> (SingI n => r) -> r,可让您自由地从Sing n转换为SingI n(另一个方向就是sing)。(这些全部在中提供Data.Singletons。)其中也有一些Template Haskell Data.Singletons.TH,可让您编写

singletons [d|data Currency = USD | EUR | YEN|]

在程序的顶层,以便定义Currency类型以及相应的SingSingI实例。(您需要以下语言扩展启用,也:KindSignaturesDataKindsTypeFamiliesGADTsExistentialQuantificationScopedTypeVariablesTemplateHaskell)。

这真的很强大–如果斜视它,几乎就像依赖类型一样–但是使用起来可能会非常麻烦。的确,如果您想了解更多信息,则有一篇论文在谈论这一话题: Sam Lindley和Conor McBride撰写的“ Hasochism:依赖类型的Haskell编程的乐趣和痛苦”任何已经在考虑这些想法的人都可以读懂,尽管材料本质上是棘手的。请注意,它们的符号略有不同。不幸的是,我不知道关于这些东西的任何不错的博客文章或教程风格的介绍。


¹不过,我不确定该类型统一规则在类型族中的地位。

²否则,包含的运行时词典sing将不会传入,因此该值在运行时将不可用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

我可以使用返回值作为参数多次运行同一个函数吗?

我可以使一个函数返回列表中的多个值吗?

我可以使用成员函数作为EnumWindows的第一个参数吗

我可以使用lambda指针数组来保存具有不同类型的参数/返回值的函数吗?

我可以使一个函数在Vala中处理多个数据类型参数吗?

我可以编写一个可以在C中返回一种数据类型或另一种数据类型的函数吗?

我可以使用类型作为值(或从构造函数参数正确推断通用类类型)吗?

您可以创建一个可以使用参数结果指定要更改的值的函数吗?

使用“ apply”时,我可以仅返回一个函数的值吗?

我可以在php中使用多个可选参数编写函数,但至少要使用一个HAS吗?

我可以使用lambda调用将路径参数传递给另一个lambda函数吗?

我可以使用 if...else 条件返回一个操作作为 lambda 函数的输出吗?

我们可以使用尾随返回类型语法编写构造函数/析构函数吗?

我可以从一个方法返回多个值而不为返回类型或 OUT 参数创建一个类吗?

我们可以编写一个以元素为参数并返回结果的泛型方法吗?(例如返回 isDisplayed() 函数结果的方法)?

我可以使用一个使用不同表参数的触发器吗?

我们可以编写一个函数来返回指向函数模板的函数指针吗?

可以编写一个Scala函数,使其获得任何函数并以相反的参数返回它吗?

我可以使用类对象实例化一个类吗?构造函数呢?

我可以使用一个函数来更改React中的不同状态吗?

我可以使用多线程多次调用一个简单的函数吗?C ++ 11

我可以使用另一个类内部的函数中的变量吗?

我想编写一个返回名字和第二个名字的第一个索引的函数,我可以使用切片但我希望代码自动工作,JS

我可以使用另一个 Annotation 作为 Spring @Qualifier 的值吗?

试图编写一个可以接受可选参数并根据其类型过滤,返回图标的函数

编写可能最终传递任何类型的结构的函数时,可以使用接口作为参数吗?

我可以编写返回函数的函数类型吗?

编写一个函数,通过判断每个参数的类型来自动确定其返回值

我可以构造一个既可以使用值又可以引用特征的结构吗?