Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 在不使用特设解析器实现的情况下反序列化许多网络消息_Haskell_Deserialization - Fatal编程技术网

Haskell 在不使用特设解析器实现的情况下反序列化许多网络消息

Haskell 在不使用特设解析器实现的情况下反序列化许多网络消息,haskell,deserialization,Haskell,Deserialization,我有一个关于反序列化的问题。我可以设想一个使用Data.Data、Data.Typeable或使用GHC.Generics的解决方案,但我很好奇它是否可以在没有泛型、SYB或元编程的情况下实现 问题描述: 给定已知包含本地定义的代数数据类型字段的[String]列表,我想对[String]进行反序列化以构造目标数据类型。我可以编写一个解析器来实现这一点,但我正在寻找一个通用的解决方案,它可以反序列化程序中定义的任意数量的数据类型,而无需为每种类型编写解析器。通过了解代数类型的值构造函数的数量和类

我有一个关于反序列化的问题。我可以设想一个使用Data.Data、Data.Typeable或使用GHC.Generics的解决方案,但我很好奇它是否可以在没有泛型、SYB或元编程的情况下实现

问题描述:

给定已知包含本地定义的代数数据类型字段的[String]列表,我想对[String]进行反序列化以构造目标数据类型。我可以编写一个解析器来实现这一点,但我正在寻找一个通用的解决方案,它可以反序列化程序中定义的任意数量的数据类型,而无需为每种类型编写解析器。通过了解代数类型的值构造函数的数量和类型,只需对每个字符串执行读取,即可生成构建类型所需的适当值。然而,我不想使用泛型、反射、SYB或元编程(除非不可能)

假设我定义了大约50种类似的类型(所有简单的代数类型都由基本原语组成(没有嵌套或递归类型,只是原语的不同组合和顺序):

我可以使用在每个[String]之前解析的标记id来确定与通过网络接收的[String]关联的数据类型

可能的推测解决方案路径:

由于数据构造函数是Haskell中的一级值,并且实际上有一个类型——NetworkMsg构造函数可以被视为一个函数吗,例如:

NetworkMsg :: Int -> Int -> Double -> NetworkMsg
我是否可以使用uncurryN将此函数转换为元组上的函数,然后将[String]复制到该函数现在采用的相同形状的元组中

NetworkMsg' :: (Int, Int, Double) -> NetworkMsg
我认为这是行不通的,因为我需要了解值构造函数和类型信息,这需要数据、可类型、反射或其他元编程技术


基本上,我正在寻找许多类型的自动反序列化,而无需编写类型实例声明或在运行时分析类型的形状。如果不可行,我将用另一种方法来实现。

您是正确的,构造函数本质上只是函数,因此您可以通过只是为函数编写实例。您仍然需要编写一个单独的实例 但是,对于所有不同数量的参数

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

import Text.Read
import Control.Applicative

class FieldParser p r where
    parseFields :: p -> [String] -> Maybe r

instance Read a => FieldParser (a -> r) r where
    parseFields con [a] = con <$> readMaybe a
    parseFields _ _ = Nothing

instance (Read a, Read b) => FieldParser (a -> b -> r) r where
    parseFields con [a, b] = con <$> readMaybe a <*> readMaybe b
    parseFields _ _ = Nothing

instance (Read a, Read b, Read c) => FieldParser (a -> b -> c -> r) r where
    parseFields con [a, b, c] = con <$> readMaybe a <*> readMaybe b <*> readMaybe c
    parseFields _ _ = Nothing

{- etc. for as many arguments as you need -}
不过,我想知道如何在应用程序中准确地使用消息类型,因为将每个消息作为其单独的类型会使使用任何类型的通用消息处理程序变得非常困难

是否有一些原因可以解释为什么您不只是拥有单一的消息数据类型?例如

data NetworkMsg
    = NetworkMsg1 {fieldA :: Int}
    | NetworkMsg2 {fieldB :: Int, fieldC :: Float}
现在,虽然实例是以几乎相同的方式构建的,但是由于结果类型总是已知的,因此可以得到更好的类型推断

instance Read a => MessageParser (a -> NetworkMsg) where
    parseMsg con [a] = con <$> readMaybe a

instance (Read a, Read b) => MessageParser (a -> b -> NetworkMsg) where
    parseMsg con [a, b] = con <$> readMaybe a <*> readMaybe b

instance (Read a, Read b, Read c) => MessageParser (a -> b -> c -> NetworkMsg) where
    parseMsg con [a, b, c] = con <$> readMaybe a <*> readMaybe b <*> readMaybe c

parseMessage :: String -> [String] -> Maybe NetworkMsg
parseMessage tag fields = case tag of
    "NetworkMsg1" -> parseMsg NetworkMsg1 fields
    "NetworkMsg2" -> parseMsg NetworkMsg2 fields
    _ -> Nothing
instance Read a=>MessageParser(a->NetworkMsg)其中
parseMsg con[a]=con reada
实例(读a,读b)=>MessageParser(a->b->NetworkMsg),其中
parseMsg con[a,b]=con readMaybe a readMaybe b
实例(读a,读b,读c)=>MessageParser(a->b->c->NetworkMsg),其中
parseMsg con[a,b,c]=con readMaybe a readMaybe b readMaybe c
parseMessage::String->[String]->可能是NetworkMsg
parseMessage标记字段=的大小写标记
“NetworkMsg1”->parseMsg NetworkMsg1字段
“NetworkMsg2”->parseMsg NetworkMsg2字段
_->没有

我也不确定为什么你不想使用任何泛型的工具来具体地做类型泛型编程。GHC。泛型,Syb或模板Haskell通常是解决这类问题的最佳方案。

我会认为自己是Haskell新手,所以也许我忽略了一些显而易见的东西。我不太明白。使用
Traversable
NetworkMsg
不能是那样,或者
Applicative
,因为它的类型不匹配。我也不清楚您在这里要求什么。您想将
[String]
解析为元组(或元组的新类型)吗,其中列表中的每个
字符串都编码一个元组字段?@AndrásKovács,谢谢,我修改了这个问题,试图让它更清楚。我提出了在每个数据类型上编写一个Applicative实例的想法,因为我认为它可以让我执行一系列读取并将结果合并到类型中,但我只是不知道instorming.Shang,这正是我要找的!谢谢。我确实有一个主消息类型,它代表所有消息类型构造函数的代数和类型。我想这样做,部分是出于好奇,部分是因为我希望有一些习惯用法和更少的库依赖性。
data NetworkMsg
    = NetworkMsg1 {fieldA :: Int}
    | NetworkMsg2 {fieldB :: Int, fieldC :: Float}
instance Read a => MessageParser (a -> NetworkMsg) where
    parseMsg con [a] = con <$> readMaybe a

instance (Read a, Read b) => MessageParser (a -> b -> NetworkMsg) where
    parseMsg con [a, b] = con <$> readMaybe a <*> readMaybe b

instance (Read a, Read b, Read c) => MessageParser (a -> b -> c -> NetworkMsg) where
    parseMsg con [a, b, c] = con <$> readMaybe a <*> readMaybe b <*> readMaybe c

parseMessage :: String -> [String] -> Maybe NetworkMsg
parseMessage tag fields = case tag of
    "NetworkMsg1" -> parseMsg NetworkMsg1 fields
    "NetworkMsg2" -> parseMsg NetworkMsg2 fields
    _ -> Nothing