What should I do when I feel like using text polymorphic messages in Haskell?

I have the following question for this question . What does the idiomatic Haskell mean, equivalent to a polymorphic class-level constant in an object-oriented language?


I am experimenting with event-sourcing using the Event Store and Haskell. I am stuck trying to understand the logic that stores and loads events.

Event Store is based on the concept of event streams; in an object-oriented domain model, there is usually a 1: 1 relationship between event flows and aggregates. You can organize streams into categories; usually you will have one category for each class in your domain model. Here is an example of how you can model it in C #:

interface IEventStream<T> where T : Event
{
    string Category { get; }
    string StreamName { get; }
    IEnumerable<T> Events { get; }
}

class PlayerEventStream : IEventStream<PlayerEvent>
{
    public string Category { get { return "Player"; } }
    public string StreamName { get; private set; }
    public IEnumerable<PlayerEvent> Events { get; private set; }
    public PlayerEventStream(int aggregateId)
    {
        StreamName = Category + "-" + aggregateId;
    }
}

class GameEventStream : IEventStream<GameEvent>
{
    public string Category { get { return "Game"; } }
    public string StreamName { get; private set; }
    public IEnumerable<GameEvent> Events { get; private set; }
    public GameEventStream(int aggregateId)
    {
        StreamName = Category + "-" + aggregateId;
    }
}

class EventStreamSaver
{
    public void Save(IEventStream stream)
    {
        CreateStream(stream.StreamName);
        AddToCategory(stream.StreamName, stream.Category);
        SaveEvents(stream.StreamName, stream.Events);
    }
}

, GameEvent Player . Category .

Haskell:

data EventStream e = EventStream AggregateID [e]

streamName :: Event e => EventStream e -> String
streamName (EventStream aggregateID (e:events)) = (eventCategory e) ++ '-':(toString aggregateID)

class Event e where
    eventCategory :: e -> String
    -- and some other functions related to serialisation

instance Event PlayerEvent where
    eventCategory _ = "Player"

instance Event GameEvent where
    eventCategory _ = "Game"

saveEventStream :: Event e => EventStream e -> IO ()
saveEventStream stream@(EventStream id events) =
    let name = streamName stream
        category = eventCategory $ head events
    in do
        createStream name
        addToCategory name category
        saveEvents name events

. eventCategory e - , . , ( ).

, # Haskell - ?


. , , , , ( ) do:

type StreamName = String
type CategoryName = String

createStream :: StreamName -> IO ()
addToCategory :: StreamName -> CategoryName -> IO ()
saveEvents :: Event e => StreamName -> [e] -> IO ()

- .

+4
2

, , .

,

data EventStream e = EventStream AggregateID [e]

streamName :: Event e => EventStream e -> String
streamName (EventStream aggregateID (e:events)) = (eventCategory e) ++ '-':(toString aggregateID)

. eventCategory , , . , , eventCategory . , eventCategory undefined.

- eventCategory:

data Proxy p = Proxy 

class Event e where
    eventCategory :: Proxy e -> String

, . , eventCategory , .

, #, : - , :

{-# LANGUAGE MultiParamTypeClasses #-}

import Data.ByteString 

class Event e where 
  deserialize :: ByteString -> e 
  ... other stuff

class Event e => EventStream t e where 
  category :: t e -> String
  aggregateId :: t e -> Int 
  events :: t e -> [e] 
  name :: t e -> String 
  name s = category s ++ "-" ++ show (aggregateId s)

EventStream typeclass .

, name , , , . , , , .

:

data PlayerEvent = ...
instance Event PlayerEvent where ...

data GameEvent = ...
instance Event GameEvent where ...

:

data PlayerEventStream e = PES Int [e] 
instance EventStream PlayerEventStream PlayerEvent where 
  category = const "Player" 
  aggregateId (PES n _) = n
  events (PES _ e) = e 

data GameEventStream e = GES Int [e] 
instance EventStream GameEventStream GameEvent where 
  category = const "Game" 
  aggregateId (GES n _) = n
  events (GES _ e) = e 

, , . PlayerEvent GameEventStream (, , EventStream GameEventStream, PlayerEvent s). :

class Event e => EventStream t e | t -> e where 

, , - :

instance EventStream PlayerEventStream PlayerEvent where 
instance EventStream PlayerEventStream GameEvent where 

:

saveEventStream :: EventStream t e => t e -> IO ()
saveEventStream s = do 
  createStream (name s)
  addToCategory (name s) (category s) 
  saveEvents (name s) (events s) 

, , , , , , #.

+5

, . , .

-, , . :

type CategoryName = String
type Name = String
type PlayerID = Int
type StreamName = String
data Move = Left | Right | Wait

data PlayerEvent = PlayerCreated Name | NameUpdated Name
data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move

. e GameEvent, PlayerEvent.

data EventStream e = EventStream
    { category :: String
    , name :: String
    , events :: [e]
    }

# , Category. Haskell . # , Haskell EventStream e. EventStream, :

-- generalized smart constructor
mkEventStream :: CategoryName -> Int -> EventStream e
mkEventStream cat ident = EventStream cat (cat ++ " - " ++ show ident) []

playerEventStream :: Int -> EventStream PlayerEvent
playerEventStream = mkEventStream "Player"

gameEventStream :: Int -> EventStream GameEvent
gameEventStream = mkEventStream "Game"

, . :

createStream :: StreamName -> IO ()
createStream = undefined

addToCategory :: StreamName -> CategoryName -> IO ()
addToCategory = undefined

saveEvents :: EventStream e -> IO ()
saveEvents = undefined

saveEventStream :: EventStream e -> IO ()
saveEventStream stream = do
    createStream name'
    addToCategory name' (category stream)
    saveEvents stream
  where
    name' = name stream

, #. , , .

+3

Source: https://habr.com/ru/post/1528852/


All Articles