在函数上传递数据构造函数而不是Type构造函数

时间:2019-01-07 15:17:08

标签: haskell

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

我想将一个特定的命令处理为一个功能,  例如使用伪代码:

{-# LANGUAGE DataKinds #-}
handle :: 'CreateWorkspace -> CommandDirective GsdState

我本以为可以将值构造函数提升为类型,但实际上我不知道该怎么做...

2 个答案:

答案 0 :(得分:4)

解决方案1:不要幻想,请使用Haskell 98兼容类型

我建议的第一个解决方案是使用单独的记录类型。这不需要任何花哨的类型的系统黑客,并且避免了记录字段名称中隐含的部分功能。

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

此外:实际上,我支持Haskell消除我们所知道的data而改为:

  • Record用于具有字段名称的单个构造函数类型
  • data,没有字段名称,但具有多个构造函数。

解决方案2:使用GADT和DataKinds

对于您的示例,此解决方案有些过高,但是也许您的实际问题有一段代码中未提到的需求。这个想法是创建一个新的数据类型,为您的构造函数定义标记,并将这些构造函数用作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

答案 1 :(得分:2)

也许这种方法足以满足您的需求。以下是Either类型的变体,但标记了L(左)和R(右)注解以及投影lr

{-# 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

函数foo和投影l仅对左值有效,而r仅对右值有效。