在Haskell中读取长数据结构

Arc*_*gel 6 haskell

我必须从文本文件(空格分隔)中读取数据结构,每行一个数据项.我的第一个尝试是

data Person = Person {name :: String, surname :: String, age :: Int, ... dozens of other fields} deriving (Show,...)

main = do
  string <- readFile "filename.txt"
  let people = readPeople string
  do_something people

readPeople s = map (readPerson.words) (lines s)

readPerson row = Person (read(row!!0)) (read(row!!1)) (read(row!!2)) (read(row!!3)) ... (read(row!!dozens))
Run Code Online (Sandbox Code Playgroud)

这段代码有效,但代码readPerson很糟糕:我必须复制粘贴read(row!!n))数据结构中的所有字段!

所以,作为第二次尝试,我认为我可能会利用Person函数的Currying ,并在当时传递一个参数.

嗯,Hoogle肯定有东西,但我无法弄清楚签名的类型......没关系,它看起来很简单,我可以自己编写:

readPerson row = readFields Person row

readFields f [x] = (f x)
readFields f (x:xs) = readFields (f (read x)) xs
Run Code Online (Sandbox Code Playgroud)

啊,编码风格看起来好多了!

但是,它不编译! Occurs check: cannot construct the infinite type: t ~ String -> t

实际上,f我传递给的函数readFields在每次调用时都有不同的类型签名; 这就是为什么我无法想出它的类型签名......

所以,我的问题是:读取具有多个字段的数据结构的最简单和优雅的方法是什么?

beh*_*uri 2

编辑:如果您正在读取字符串,则解决方案更简单:

{-# LANGUAGE FlexibleInstances #-}

data Person = Person { name :: String, age :: Int, height :: Double }
    deriving Show

class Person' a where
    person :: a -> [String] -> Maybe Person

instance Person' Person where
    person c [] = Just c
    person _ _  = Nothing

instance (Read a, Person' b) => Person' (a -> b) where
    person f (x:xs) = person (f $ read x) xs
    person _ _      = Nothing

instance {-# OVERLAPPING #-} Person' a => Person' (String -> a) where
    person f (x:xs) = person (f x) xs
    person _ _      = Nothing
Run Code Online (Sandbox Code Playgroud)

然后,如果列表大小合适,您将得到:

\> person Person $ words "John 42 6.05"
Just (Person {name = "John", age = 42, height = 6.05})
Run Code Online (Sandbox Code Playgroud)

如果没有,你什么也得不到:

\> person Person $ words "John 42"
Nothing
Run Code Online (Sandbox Code Playgroud)

当所有记录字段都属于同一类型时,构造具有多个字段的 Haskell 数据类型提供了一种解决方案。如果不是,稍微多态的解决方案将是:

{-# LANGUAGE FlexibleInstances, CPP #-}

data Person = Person { name :: String, age :: Int, height :: Double }
    deriving Show

data Val = IVal Int | DVal Double | SVal String

class Person' a where
    person :: a -> [Val] -> Maybe Person

instance Person' Person where
    person c [] = Just c
    person _ _  = Nothing

#define PERSON(t, n)                                \
instance (Person' a) => Person' (t -> a) where {    \
    person f ((n i):xs) = person (f i) xs;          \
    person _ _ = Nothing; }                         \

PERSON(Int,    IVal)
PERSON(Double, DVal)
PERSON(String, SVal)
Run Code Online (Sandbox Code Playgroud)

然后,

\> person Person [SVal "John", IVal 42, DVal 6.05]
Just (Person {name = "John", age = 42, height = 6.05})
Run Code Online (Sandbox Code Playgroud)

为了构造Val类型,您可以创建另一个类型类并创建所需的实例:

class Cast a where
    cast :: a -> Val

instance Cast Int    where cast = IVal
instance Cast Double where cast = DVal
instance Cast String where cast = SVal
Run Code Online (Sandbox Code Playgroud)

那么,它的表示法会稍微简单一些:

\> person Person [cast "John", cast (42 :: Int), cast 6.05]
Just (Person {name = "John", age = 42, height = 6.05})
Run Code Online (Sandbox Code Playgroud)