GHC的DataKinds
扩展允许定义由某些数据参数化的类型。有没有可能的方法在函数定义的RHS上使用这些参数?例如,在Agda中,我可以直接从向量的类型中提取向量的长度,而无需计算其构造函数:
length : Vec A n -> Nat
length {n = n} _ = n
在Haskell中有没有可能做到这一点?
我对此感兴趣的原因是因为我认为这对我正在做的一个项目很有帮助,这个项目是一个定制的Haskell库,用于以类型安全的方式调用Java(尽可能安全)。我想不是用单个类型表示所有Java对象,而是用Java类名作为字符串(例如JObject "java.math.BigDecimal"
),因此Java中不同类的示例在Haskell中的表示形式也会不同。如果我在这里所要求的是可能的,那么它将允许从给定的Haskell方法类型自动计算JNI类型签名字符串,这样我就可以编写如下代码
method <- findMethod "toString" :: IO (JMethod (JObject "java.math.BigDecimal") (IO JString))
3条答案
按热度按时间ac1kyiln1#
在Haskell中,类型被擦除,它们在运行时不存在。
DataKinds
允许您在类型中使用数据构造函数,但它们仍然会被擦除。在forall n. Vec A n -> Nat
类型的函数中,不可能返回n
,因为它将被擦除。为了使
n
不被擦除,您将拥有一个依赖于运行时值n
的类型Vec A n
,这就是依赖类型的含义。依赖类型还不是Haskell的原生特性。但是,可以使用singletons模拟它们。
length
的类型将是forall n. SingI n => Vec A n -> Nat
,其中SingI
约束提供n
的运行时反射。ni65a41a2#
在Haskell中,类型在运行时不存在。仅用于源代码中的表达式分类,不用于运行时的值分类。
当然,你可以使用
data JObject (className :: Symbol) = ...
,并使用它来强制一个只适用于某个特定Java类对象的函数,永远不会为另一个类名指定JObject
。您甚至可以确保组合了同一类的两个Java对象的代码永远不会给JObject
的不匹配类,即使在不知道实际类名是什么的多态上下文中也是如此。但所有这些都是关于你的 * 代码 *。在运行时,当内存中存在实际的JObject
时,它只是不包含className
。没什么可提取的。然而,它**是可能的,使你可以访问
className
,就好像它是一个值而不是一个类型。您需要的是一个可以调用的函数,该函数在className
Symbol
上进行参数化,该函数将生成运行时String
值。在Haskell中,创建一个对类型参数的每个可能值都有不同行为的函数的标准方法是使用类型类。你只需要一个类,它的方法给你一个String
值,然后为每个可能的Symbol
提供一个示例,通过返回相应的String
来实现这个方法。很简单!幸运的是,GHC实际上为每个
Symbol
提供了一个内置类,其中包含魔术示例,因此没有人需要手工编写所有这些示例。它被称为KnownSymbol
。方法symbolSing @s
实际上给出了一个SSymbol s
,而不是直接给出String
,但您可以通过调用fromSymbol
来获得它。KnownSymbol
类除了神奇地拥有无限数量的离散示例之外,它是一个非常普通的类。这意味着,如果您所拥有的只是一个未知Symbol
上的参数化类型,则无法自动获取KnownSymbol
示例;您必须确保在函数上使用正确的约束,以便从实际选择Symbol
的KnownSymbol
示例流到您想要知道它是什么的地方。SSymbol
类型是一种称为“单例”的技术(不要与OO语言中的“单例模式”混淆)。它只意味着一个参数化的类型,其值与类型参数的选择一一对应。即SSymbol s
是Symbol
s
的每个选择的单独类型;这些类型中的每一个都只有一个值(因此称为“单例”),因此知道我们拥有哪个值可以揭示其对应的Symbol
是什么,即使Symbol
在技术上在运行时不存在。使用这些单例类型作为链接允许比仅仅具有直接链接类型和值的类更多的选项,即使在简单情况下不一定需要。如果您感兴趣,singletons
library是一个更一般地定义和使用单例类型的框架(与base
提供的极少数类型相反,因为它们需要编译器支持“魔术示例”)。i7uq4tfw3#
这实际上是可行的。演示:
解释。
1.在类型上下文中,字符串字面量被提升为
GHC.Types.Symbol
,因此我们需要使用它作为我们的数据类型。KnownSymbol a =>
正是在运行时不会擦除a
的对象。它拖动一个字典,可以用来恢复字符串(通过Proxy
)。(This可能不是最惯用的方法,我不是一个Maven在野生和美妙的Haskell扩展)。