用例:跨多个消息服务跟踪用户的应用程序。有一个TwitterAccount
数据类型,一个FacebookAccount
数据类型等等。这些可以很容易地与一个Account
sum-type连接在一起,但层次结构的下一级导致我的问题。如何在不使用求和类型或副本的情况下创建异构数据类型的列表
一个TwitterAccount
有TwitterPost
个列表,FacebookAccount
有FacebookPost
S,列表等
我的任务:我希望能够把所有的职位在过去10天内所有帐户放入单个列表中,并从中提取常用时间和消息正文字段以供显示。
我没办法:我想,如果每个类的Post
实现的类型类像SimplePost
曝光功能messageBody
和messageTime
这可能会解决我的问题,但我不能创建的[SimpleMessage]
列表。
我要保持不变,一个TwitterAccount
只能包含TwitterPost
S,等等,所以我不能使用和类型。我不想创建对象的副本来执行此操作。
这个问题最好,最干净,最Haskell-ish设计是什么?
UPDATE 这不是一个答案,但作为替代由recursion.ninja和埃尔德·佩雷拉我一直在想,如果我可以用幻影类型的满足我的不变量提供了四种解决方案,并Account
和Post
类型,它包含所有提供者所需的所有可能的信息。然而元组的使用和尴尬的逻辑意味着它不能很好地扩展;也许这应该是一个不同的问题。
{-# LANGUAGE EmptyDataDecls #-}
-- Some FSharpisms
(|>) = flip ($)
(<|) = ($)
infixr 0 <|
data Twitter
data Facebook
data LinkedIn
data Post a = Post{
postBody :: String,
postDate :: UTCTime,
postForwarded :: Bool,
postFriendMentions :: [UserName]
} deriving (Show, Eq)
data Account a = Account {
accountName :: String,
accountPosts :: [Post a]
} deriving (Show, Eq)
data User = User {
userName :: String,
userTweets :: Account Twitter,
userFaces :: Account Facebook,
userLinks :: Account LinkedIn
}
prettyShowUtc :: UTCTime -> String
prettyShowUtc utc = ...
prettyShow :: Post a -> String
prettyShow p = prettyShowUtc (postDate p) ++ " : " ++ show (postBody p)
showOrderedOf2 :: ([Post a], [Post b]) -> [String]
showOrderedOf2 ([], []) = []
showOrderedOf2 (ls, []) = map prettyShow ls
showOrderedOf2 ([], rs) = map prettyShow rs
showOrderedOf2 ((l:ls), (r:rs)) =
if postDate l < postDate r
then prettyShow l : showOrderedOf2 (ls, (r:rs))
else prettyShow r : showOrderedOf2 ((l:ls), rs)
showOrderedOf3 :: ([Post a], [Post b], [Post c]) -> [String]
showOrderedOf3 ([], [], []) = []
showOrderedOf3 (as, [], []) = map postBody as
showOrderedOf3 ([], bs, []) = map postBody bs
showOrderedOf3 ([], [], cs) = map postBody cs
showOrderedOf3 (as, bs, []) = showOrderedOf2 (as, bs)
showOrderedOf3 ([], bs, cs) = showOrderedOf2 (bs, cs)
showOrderedOf3 (as, [], cs) = showOrderedOf2 (as, cs)
showOrderedOf3 ((a:as), (b:bs), (c:cs)) =
let (adate, bdate, cdate) = (postDate a, postDate b, postDate c)
minDate = minimum [adate, bdate, cdate]
in
if adate == minDate
then prettyShow a : showOrderedOf3 (as, (b:bs), (c:cs))
else (if bdate == minDate
then prettyShow b : showOrderedOf3 ((a:as), bs, (c:cs))
else prettyShow c : showOrderedOf3 ((a:as), (b:bs), cs))
createAndShowSample :: IO()
createAndShowSample =
let faceAc = Account {...} :: Account Facebook
twitAc = Account {...} :: Account Twitter
linkAc = Account {...} :: Account LinkedIn
in
showOrderedOf3 (accountPosts faceAc, accountPosts twitAc, accountPosts linkAc)
|> intercalate "\n"
|> putStrLn
使它们成为某些类型类的两个实例,然后启用'ExistencialTypes' laguange扩展。 –
这是最好的Haskell练习,因为它只能通过GHC扩展提供吗?是否有任何“纯粹”的Haskell方式来做到这一点? – Feenaboccles
这是一个GHC扩展,请参阅我的答案*正确的方式* –