如果以下内容是基于我的误解,我提前道歉。我在任何地方都找不到我需要的答案。
我正在实现一个从文件中读取序列化对象的程序,全程使用惰性求值。我首先将文件作为一个Lazy ByteString读取,然后使用Get-Monad对其进行解析。在文件中,有一个点存储类型描述符,另一个点存放数据。类型描述符告诉我如何解释数据,以及它最终具有哪种类型,并且该类型可以采用嵌套形式;Double或[[Word8]是两种可能性。
现在,我想到的这个想法(听起来非常优雅)如下:如果方法(解析类型描述符)创建了一个Get-Monad,但不运行它,然后可以用保存数据的ByteString运行,该怎么办?
这就需要这样一种方法:
parseTypeDescriptor :: Get (Get a)
其中,a与描述符描述的类型相同(意味着它可以采用嵌套形式)。以下是目前代码的一些部分:
parseTypeDescriptor :: Get (Get a)
-- first part of the type descriptor is an id (Word8) that implies a type
parseTypeDescriptor = getWord8 >>= go
where go 0 = return getWord8
go 1 = return getWord16be
go 2 = return getWord32be
...
-- id 5 indicates that the type is an array
-- this means two more values are coming;
-- the first indicates the array's length, the second its type
go 5 = do n <- getWord8
action <- parseTypeDescriptor
return $ -- TODO --
"action"的类型应为"Get a"。在TODO中,我需要构造一个执行n次操作的值,将这些值放入一个数组中,并在该数组周围放置一个Get。
示例:如果action=getWord16be并且n=2;那么TODO应该等价于:
TODO :: Get [a]
TODO = do x <- getWord16be
y <- getWord16be
return [x,y]
我有3个问题:
- 我不知道TODO上的什么代码能满足我的要求
- 编译器告诉根方法(parseTypeDescriptor)不能返回多个类型。如果它返回Get(Get Word8),它就不能还返回Get(Get[Word8])
- 我不确定运行顶级Get时是否可以避免它创建的Gets也在运行
我开始怀疑这实际上是不可能的,但我希望我错了——我的解释是可以理解的。
你并没有真正返回多态的东西(通用量化)——事实上,你总是返回一个具体的类型Get A
,但它只会在运行时决定它是什么。这被称为存在量化。Haskell98没有任何这样的东西,但您可以使用广义代数数据类型:
{-# LANGUAGE GADTs #-}
data GetSomething where
GetSomething :: Get a -> GetSomething
parseTypeDescriptor :: Get GetSomething
-- first part of the type descriptor is an id (Word8) that implies a type
parseTypeDescriptor = getWord8 >>= go
where go 0 = return $ GetSomething getWord8
go 1 = return $ GetSomething getWord16be
go 2 = return $ GetSomething getWord32be
到目前为止还不错。。。问题是,使用GetSomething
实际上没有任何有用的方法,因为无法知道它实际产生的类型(它隐藏在GADT中)。有一些方法可以克服这一点——例如,如果所有解析的类型都属于一个公共类型类,则可以将其添加为约束:
data GetSomething where
GetSomething :: CommonClass a => Get a -> GetSomething
这样,即使不知道确切的类型,您也可以解析值并对其进行处理。
然而,这可能不是最好的方法:基本上,你必须在丢失类型信息后运行,然后四处破解才能完成任务。在Haskell中,存在主义被认为是一种反模式。
更好的解决方案可能是将类型放在一个清晰的替代数据结构中:
data GetSomething
= GetW8 (Get Word8)
| GetW16 (Get Word16)
| ...
| GetList [GetSomething]
然后,你可以简单地进行模式匹配,找出你拥有的特定类型,因为支持的类型列表是有限的,你可以确保你不会因为一个类型实际上不受支持而遇到问题。