haskell - How to create a list of heterogeneous datatypes without using sum-types or copies -
the use-case: app follow users across several messaging services. there's twitteraccount
datatype, facebookaccount
datatype, etc. these joined account
sum-type, next level of hierarchy leads problem.
a twitteraccount
has list of twitterpost
s, facebookaccount
has list of facebookpost
s, etc.
my task: want able put posts last 10 days accounts single list, , extract common time , message body fields them display.
my failed approach: thought if every class of post
implemented typeclass simplepost
exposing functions messagebody
, messagetime
that might solve problem, can't create list of [simplemessage]
.
i want maintain invariant twitteraccount
can containtwitterpost
s, , on, can't use sum-types. i'd prefer not create copies of objects this.
what best, cleanest, haskell-ish design problem?
update isn't answer, alternative 4 solutions provided recursion.ninja , helder pereira i've been thinking if can meet invariants using phantom types, , account
, post
types hold possible information required providers. use of tuples, , awkward logic, means doesn't scale well; perhaps should in different question.
{-# language emptydatadecls #-} -- fsharpisms (|>) = flip ($) (<|) = ($) infixr 0 <| data twitter data facebook data linkedin data post = post{ postbody :: string, postdate :: utctime, postforwarded :: bool, postfriendmentions :: [username] } deriving (show, eq) data account = 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 -> 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 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 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 prettyshow : showorderedof3 (as, (b:bs), (c:cs)) else (if bdate == mindate 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
you should abstract facebookaccount
, twitteraccount
instances of socialmediaaccount
haskell code:
import control.applicative ((<$>)) import data.list import data.ord import data.time data facebookaccount = facebookaccount [facebookpost] data twitteraccount = twitteraccount [twitterpost] data facebookpost = facebookpost string utctime data twitterpost = twitterpost string utctime data socialmediaaccount = socialmediaaccount { accountposts :: [socialmediapost] } data socialmediapost = socialmediapost { postbody :: string , posttime :: utctime } class socialmedia simpleaccount :: -> socialmediaaccount instance socialmedia facebookaccount simpleaccount (facebookaccount xs) = socialmediaaccount $ f <$> xs f (facebookpost text time) = socialmediapost text time instance socialmedia twitteraccount simpleaccount (twitteraccount xs) = socialmediaaccount $ f <$> xs f (twitterpost text time) = socialmediapost text time getallmessages :: (socialmedia a, socialmedia b) => -> b -> [socialmediapost] getallmessages xs ys = sortby (comparing posttime) $ extract xs ++ extract ys extract :: socialmedia => -> [socialmediapost] extract = accountposts . simpleaccount
Comments
Post a Comment