在运行时动态生成Haskell类型? [英] Dynamically generate Haskell types at runtime?

查看:73
本文介绍了在运行时动态生成Haskell类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以在运行时根据给定的模板定义Haskell类型吗?这就是我的意思.假设我需要一个限制在一定范围内的整数类型(在编译时未知).我还想要一个功能,

Can one define a Haskell type at runtime from a given template? Here's what I mean by this. Suppose I need an integer type that is restricted to some range (unknown precisely at compile time). I also want a feature that:

succ 0 = 1
succ 1 = 2
...
succ n = 0

n在编译时未知.我可以做这样的事情:

n being unknown at compile time. I could do something like this:

data WrapInt = WrapInt {
        value       :: Int,
        boundary    :: Int
}

wrapInt :: Int -> Int -> WrapInt
wrapInt boundary value = WrapInt value boundary

现在,我想要保留的是wrapInt函数的原样,但要避免将边界作为值存储在WrapInt类型内.相反,我希望将它以某种方式存储在类型定义中,这当然意味着必须在运行时动态定义类型.

Now what I would like to have is to preserve the wrapInt function as it is, but to avoid storing the boundary as a value inside WrapInt type. Instead I would like it to be stored somehow in type definition, which of course means that the type would have to be defined dynamically at runtime.

是否可以在Haskell中实现这一目标?

Is it possible to achieve this in Haskell?

推荐答案

包可让您在运行时生成类型类的新本地"实例.

The reflection package lets you generate new "local" instances of a typeclass at runtime.

例如,假设我们具有以下可以包装"的值的类型类型:

For example, suppose we have the following typeclass of values that can "wrap around":

{-# LANGUAGE Rank2Types, FlexibleContexts, UndecidableInstances #-}

import Data.Reflection
import Data.Proxy

class Wrappy w where
   succWrappy :: w -> w

我们定义了一个带有幻像类型参数的新类型:

We define this newtype that carries a phantom type parameter:

data WrapInt s = WrapInt { getValue :: Int } deriving Show

将其设为Wrappy的实例:

instance Reifies s Int => Wrappy (WrapInt s) where
    succWrappy w@(WrapInt i) = 
        let bound = reflect w 
        in
        if i == bound
            then WrapInt 0
            else WrapInt (succ i)

有趣的部分是Reifies s Int约束.这意味着:幻像类型s在类型级别代表类型Int的值".用户永远不会为Reifies定义实例,这是由reflection程序包的内部机制完成的.

The interesting part is the Reifies s Int constraint. It means: "the phantom type s represents a value of type Int at the type level". Users never define an instance for Reifies, this is done by the internal machinery of the reflection package.

因此,Reifies s Int => Wrappy (WrapInt s)的意思是:只要s表示类型为Int的值,我们就可以使WrapInt sWrappy的实例".

So, Reifies s Int => Wrappy (WrapInt s) means: "whenever s represent a value of type Int, we can make WrapInt s an instance of Wrappy".

reflect 函数采用与幻像类型匹配的代理值,并返回实际的Int值,该值在实现Wrappy实例时使用.

The reflect function takes a proxy value that matches the phantom type and brings back an actual Int value, which is used when implementing the Wrappy instance.

要实际将值分配"给幻像类型,我们使用修改:

To actually "assign" a value to the phantom type, we use reify:

-- Auxiliary function to convice the compiler that
-- the phantom type in WrapInt is the same as the one in the proxy
likeProxy :: Proxy s -> WrapInt s -> WrapInt s
likeProxy _ = id

main :: IO ()
main = print $ reify 5 $ \proxy -> 
    getValue $ succWrappy (likeProxy proxy (WrapInt 5))

请注意,reify的签名禁止幻像类型转义回调,这就是为什么我们必须用getValue解开结果的原因.

Notice that the signature of reify forbids the phantom type from escaping the callback, that's why we must unwrap the result with getValue.

答案中查看更多示例,在反射GitHub存储库中.

See more examples in this answer, on in the reflection GitHub repo.

这篇关于在运行时动态生成Haskell类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆