0

I need to implement a dynamic dispatch, I've used Existential types based on this page and produced the following code :

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
module Cqrs.Command where

import Data.UUID
import Data.Time
import Data.Aeson

import Cqrs.Core
import Data.Text
import GHC.Generics

type CommandName = String

class (FromJSON command,ToJSON command , Show command) => Command_ command where
  getCommandId :: command -> CommandId
  getAggregateId :: command -> AggregateId
  getCommandName :: command -> String


data Command = forall command . Command_ command => Command command


getMyCommandName :: Command -> String
getMyCommandName command = getCommandName command

I'm not able to use functions from the typeclass Command_ on a Command datatype, the compiler complains that way :

/Users/xxx/dev/gsdFlow/src/Cqrs/Command.hs:26:28: error:
    • No instance for (Command_ Command)
        arising from a use of ‘getCommandName’
    • In the expression: getCommandName command
      In an equation for ‘getMyCommandName’:
          getMyCommandName command = getCommandName command
   |
26 | getMyCommandName command = getCommandName command
   |                            ^^^^^^^^^^^^^^^^^^^^^^
Nicolas Henin
  • 3,244
  • 2
  • 21
  • 42
  • 4
    The error is pretty descriptive in this case. You need to implement an instance for `Command`. However it's actually not possible to implement because you will have trouble with `FromJSON`. In any case, encoding OOP patterns in Haskell like this most likely not the right solution to your problem so you might want to ask a higher level question about your actual objective. – Li-yao Xia Nov 05 '18 at 14:45

1 Answers1

4

You need to unwrap the Command constructor to get at the value whose type actually is an instance of Command_.

getMyCommandName :: Command -> String
getMyCommandName (Command c) = getCommandName c

FWIW, you code reeks strongly of existential antipattern. Why not just make it

data Command = Command
       { getCommandId :: CommandId
       , getAggregateId :: AggregateId
       , getCommandName :: String }

and be done?

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • BTW, I have reproduced the approach suggested by the page linked in my question description... Are you suggesting that I should have converter instead of Polymorphism ? like function toCommand :: MyCommandFromASpecificBusinessDomain -> Command ? That is what I understand from the link you gave to me (e.g toWidget :: Window -> Widget) This is not what I need if it's the case... I need to read Commands (CQRS command sourcing) from a CommandStore and process each command with their own specific information... – Nicolas Henin Nov 05 '18 at 15:27
  • @NicolasHenin The point is your existing `Command_` class is isomorphic to a record with 3 fields. There's no value in having many different types that all must be treated the same way, rather than one type which is a record containing three values and can be customized at the value level instead of the type level. – amalloy Nov 06 '18 at 00:49
  • ok, so I'm making a CQRS library where users define their own commands, so in the context of the that library It is not aware of them, Users have to provide them.. Command_ is not isomorphic to a record with 3 fields, Command_ is just saying there is a contract where if you want to define a Command, you need to provide these functions... am I missing something ? – Nicolas Henin Nov 06 '18 at 08:12
  • @amalloy, "one type which is a record containing three values and can be customized at the value level instead of the type level." That I originally did... but then your sum type has to be compulsory defined and I cannot have a library then (something more generic)... You need to show me an example because I don't see how you are saying to me satisfy my needs... – Nicolas Henin Nov 06 '18 at 08:19
  • You can read this : https://two-wrongs.com/dynamic-dispatch-in-haskell-how-to-make-code-extendable – Nicolas Henin Nov 06 '18 at 08:27
  • even better : https://stackoverflow.com/questions/13106683/dynamic-dispatch-in-haskell/13110560#13110560 – Nicolas Henin Nov 06 '18 at 08:52
  • 1
    @NicolasHenin having a fixed `data Command= Command {i, ai, cn}` doesn't prevent users from defining their own specialised command types. It just demands that, before passing the commands to something more generic, they “convert” them to the general `Command` type, which amounts to packing the methods as function-values into the “plain-data struct”. (These function values may still have a pointer to some more specialised state object internally, but it'll be completely encapsulated in their closure.) – leftaroundabout Nov 06 '18 at 09:39
  • it took me sometimes to integrate what you 've said with my current codebase (actually a night of sleep :-) ), but it makes senses now for me... – Nicolas Henin Nov 06 '18 at 10:11