Reputation: 3334
data GsdCommand = CreateWorkspace { commandId :: CommandId , workspaceId ::WorkspaceId , workspaceName :: Text }
| RenameWorkspace { commandId :: CommandId , workspaceId ::WorkspaceId , workspaceNewName :: Text }
| SetGoal { commandId :: CommandId ,
workspaceId ::WorkspaceId ,
goalId :: GoalId ,
goalDescription :: Text} deriving Show
I would like to handle a specific command into one function, e.g in pseudo code :
{-# LANGUAGE DataKinds #-}
handle :: 'CreateWorkspace -> CommandDirective GsdState
I was thinking that I could lift value constructors into types, but I don't know how to do it actually...
Upvotes: 1
Views: 111
Reputation: 64740
Solution 1: Don't get fancy, use Haskell 98 compatible types
The first solution I recommend is using a separate record type. This doesn't require any fancy type system hackery and avoids the partial functions implicit in your record field names.
type CommandId = ()
type WorkspaceId = ()
type Text = ()
data GsdCreate = GsdCreate { gsdcCommandId :: CommandId
, gsdcWorkspaceId :: WorkspaceId
, gsdcWorkspaceName :: Text
}
deriving (Show)
data GsdCommand = CreateWorkspace GsdCreate
| RenameWorkspace { commandId :: CommandId
, workspaceId ::WorkspaceId
, workspaceNewName :: Text }
| SetGoal { commandId :: CommandId ,
workspaceId ::WorkspaceId ,
goalDescription :: Text }
deriving (Show)
type CommandDirective a = Maybe a
type GsdState = ()
handle :: GsdCreate -> CommandDirective GsdState
handle = undefined
Aside: I'm actually in favor of Haskell eliminating data
as we know it and instead having:
Record
for single constructor types with field namesdata
without field names but with multiple constructors.Solution 2: Use GADTs and DataKinds
This solution is a bit overkill for your example, but perhaps your real problem has a need that isn't presented in the snippet. The idea is to create a new data type that defines tags for your constructors and to use those constructors as types for a GADT:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
type CommandId = ()
type WorkspaceId = ()
type Text = ()
data Ty = Create | Rename | Set
data GsdCommand a where
CreateWorkspace :: CommandId -> WorkspaceId -> Text -> GsdCommand Create
RenameWorkspace :: CommandId -> WorkspaceId -> Text -> GsdCommand Rename
SetGoal :: CommandId -> WorkspaceId -> Text -> GsdCommand Set
type CommandDirective a = Maybe a
type GsdState = ()
handle :: GsdCommand Create -> CommandDirective GsdState
handle = undefined
Upvotes: 4
Reputation: 2300
Maybe this approach is enough for what you want. Below is a variant of the Either
type, but tagged with L
(left) and R
(right) annotations, and with projections l
and r
.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
module G where
data L
data R
data E d a b where
L :: {l :: a} -> E L a b
R :: {r :: b} -> E R a b
foo :: E L a b -> a
foo (L x) = x
Function foo
and the projection l
work only on left values, and r
works only on right values.
Upvotes: 2