简体   繁体   English

是否可以在状态monad变压器内存储GADT?

[英]Is it possible to store a GADT inside a State monad transformer?

Say that I have got a GADT like this: 说我有这样的GADT:

data Term a where
  Lit    :: a -> Term a
  Succ   :: Term Int -> Term Int
  IsZero :: Term Int -> Term Bool
  If     :: Term Bool -> Term a -> Term a -> Term a

Is it possible to store Succ (Lit 2) and IsZero (Succ (Lit 2)) inside the State monad transformer, as a value of the internal state? 是否可以将状态monad变压器内的Succ (Lit 2)IsZero (Succ (Lit 2))为内部状态的值?

The issue here being those two are of different types, and I don't know how the s of StateT sma should be typed. 这里的问题是那两个是不同类型的,我不知道应该如何键入StateT smas

Edit: ATerm solved the initial question of how to store different GADT in the state, the issue now is since the type is lost it seemed impossible to compare the old and new state. 编辑: ATerm解决了如何在州内存储不同GADT的初始问题,现在的问题是由于类型丢失,似乎无法比较新旧状态。

Edit: Final answer. 编辑:最终答案。

After going back and forth with @luqui, here's the full code snippet that answers this question. 和@luqui一起来回走动之后,这里是回答这个问题的完整代码片段。

Feel free to fork this repl and have a try. 随意分叉这个repl并试一试。

{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

import Data.Typeable

data Term a where
  Lit    :: a -> Term a
  Succ   :: Term Int -> Term Int
  IsZero :: Term Int -> Term Bool
  If     :: Term Bool -> Term a -> Term a -> Term a

deriving instance (Eq a) => Eq (Term a)

data ATerm where
  ATerm :: (Typeable a, Eq a) => Term a -> ATerm

instance Eq ATerm where
    ATerm t == ATerm u
        | Just t' <- cast t = t' == u
        | otherwise = False

main :: IO ()
main = return ()

Yes, you can use an existential 是的,你可以使用存在主义

data ATerm where
   ATerm :: Term a -> ATerm

which is a monotype that stores "a Term of any type". 这是一个存储“任何类型的Term ”的单一型。

However you should be aware that you will lose the type information, which I have a hunch will not cause a problem in your case. 但是你应该知道你会丢失类型信息,我预感不会导致你的问题。 If you do need to recover it, you will need to add some Typeable constraints or some other trick—hard to say without more context on what you are doing. 如果你确实需要恢复它,你将需要添加一些Typeable约束或其他一些技巧 - 如果没有更多关于你正在做的事情的上下文。

EDIT 编辑

To get the type information back, you will need to include it in ATerm 要获取类型信息,您需要将其包含在ATerm

data ATerm where
    ATerm :: (Typeable a, Eq a) => Term a -> ATerm

Sadly, this change might cause the Typeable constraint to infect a fair amount of your code. 遗憾的是,此更改可能会导致Typeable约束感染相当数量的代码。 That's just how it goes. 这就是它的方式。 We also include Eq a , since if we are comparing ATerms and do find that their types are the same, we will need to compare on that type. 我们还包括Eq a ,因为如果我们比较ATerms并确实发现它们的类型相同,我们将需要对该类型进行比较。

Then to compare two ATerm s, you first need to compare their types, then their values. 然后要比较两个ATerm ,首先需要比较它们的类型,然后比较它们的值。 This can be done with the Typeable library. 这可以使用Typeable库完成。

instance Eq ATerm where
    ATerm t == ATerm u
        | Just t' <- cast t = t' == u
        | otherwise = False

Luckily, your Term GADT doesn't hide any types. 幸运的是,你的Term GADT并没有隐藏任何类型。 If you had a case like, for example 例如,如果你有一个案例

data Term a where
    ...
    Apply :: Func a b -> Term a -> Term b

you would need to add Typeable also to any hidden variables (variables that don't appear in the result type) 您还需要将Typeable添加到任何隐藏变量(未出现在结果类型中的变量)

    Apply :: (Typeable a) => Func a b -> Term a -> Term b

Roughly, if you want to compare types, you need to have a Typeable constraint on them somewhere. 粗略地说,如果要比较类型,则需要在某处对它们进行Typeable约束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM