Haskell:HList和可选参数

时间:2012-08-22 01:30:51

标签: haskell hlist

我一直在尝试使用HList来创建记录。

我一直在使用HList-GHCSyntax中定义的运算符。

到目前为止效果非常好,允许我写这样的东西:

myRecord = 
  (param1 .=. "param1value") .*. 
  (param2 .=. "param2value") .*. 
  emptyRecord

这允许我执行以下操作:

myRecord .!. param1

以及以下内容:

myRecord .!. param3

按预期抛出编译错误。如果需要param3,这很有效,因为我得到了编译时参数检查。

但我也想处理param3是可选的情况。我怎么能这样做?


修改:以下似乎有效(Empty为空类型):

getOptional r l = (hLeftUnion r ((l .=. Empty) .*. emptyRecord)) .!. l

但我真的不知道如何在调用代码时检查Empty

1 个答案:

答案 0 :(得分:6)

定义getOptional的问题是确定结果类型。如果有人尝试:

class GetOptional r l v | r l -> v where
  getOptional :: l -> Record r -> Maybe vs

class GetOptional r l v | r l -> v where
  getOptional :: l -> Record r -> Maybe v

然后可以通过在v中查看l来确定r,但如果l不在r,那么v应该class GetOptional r l v where getOptional :: l -> v -> Record r -> v (v->w) 1}}来?选择()还是空?放弃功能依赖性会使用户在某处提供类型注释。

或许更好的方法是提供默认值(例如fromMaybe):

w

更复杂的版本可能会提供一个函数来使用现有值{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} import Data.HList.FakePrelude(HEq,HTrue,HFalse) import Data.HList.HListPrelude(HNil(HNil),HCons(HCons)) import Data.HList.GhcSyntax((.=.),(.*.)) import Data.HList.Record(Record(Record),LVPair(LVPair),emptyRecord) class GetOptional l r v where getOptional :: l -> v -> Record r -> v instance GetOptional l HNil v where getOptional _ v _ = v instance ( HEq l l' b , GetOptional' b l (HCons (LVPair l' v') r) v ) => GetOptional l (HCons (LVPair l' v') r) v where getOptional l v (Record r) = getOptional' (undefined :: b) l v r class GetOptional' b l r v where getOptional' :: b -> l -> v -> r -> v instance GetOptional' HTrue l (HCons (LVPair l v) r) v where getOptional' _ _ _ (HCons (LVPair v) _) = v instance ( GetOptional l r v ) => GetOptional' HFalse l (HCons (LVPair l' v') r) v where getOptional' _ l v (HCons _ r) = getOptional l v (Record r) data L1 = L1 data L2 = L2 e = emptyRecord f = L1 .=. True .*. emptyRecord -- test1 :: Bool test1 = getOptional L1 False f -- test2 :: Bool test2 = getOptional L1 False e -- test3 :: () test3 = getOptional L2 () f -- test4 gives a type error: -- test4 = getOptional L1 () f 和默认值{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} import Data.HList.FakePrelude(HFalse,HTrue) import Data.HList.HListPrelude(HMember,hMember) import Data.HList.GhcSyntax((.=.),(.*.)) import Data.HList.Record(RecordLabels,Record,HasField(hLookupByLabel),recordLabels,emptyRecord) -- This type is inferred properly -- getOptional :: ( RecordLabels r ls -- , HMember l ls b -- , GetOptional' b l r v ) -- => l -> v -> Record r -> v getOptional l v rec = getOptional' (hMember l (recordLabels rec)) l v rec class GetOptional' b l r v where getOptional' :: b -> l -> v -> Record r -> v instance GetOptional' HFalse l rec v where getOptional' _ _ v _ = v instance ( HasField l r v ) => GetOptional' HTrue l r v where getOptional' _ l _ r = hLookupByLabel l r data L1 = L1 data L2 = L2 e = emptyRecord f = L1 .=. True .*. emptyRecord -- test1 :: Bool test1 = getOptional L1 False f -- test2 :: Bool test2 = getOptional L1 False e -- test3 :: () test3 = getOptional L2 () f -- test4 gives a type error: -- test4 = getOptional L1 () f

这对我有用:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.HList.FakePrelude(HFalse,HTrue)
import Data.HList.HListPrelude(HMember,hMember)
import Data.HList.GhcSyntax((.=.),(.*.))
import Data.HList.Record(RecordLabels,Record,HasField(hLookupByLabel),recordLabels,emptyRecord)
import Data.HList.TypeCastGeneric1
import Data.HList.TypeEqGeneric1
import Data.HList.Label5

-- getOptional :: ( RecordLabels r ls
--                , HMember l ls b
--                , GetOptional' b l r v )
--               =>  l -> Record r -> Maybe v
getOptional l rec = getOptional' (hMember l (recordLabels rec)) l rec

class GetOptional' b l r v where
  getOptional' :: b -> l -> Record r -> Maybe v

instance GetOptional' HFalse l rec v where
  getOptional' _ _ _ = Nothing

instance ( HasField l r v )
         => GetOptional' HTrue l r v where
  getOptional' _ l r = Just (hLookupByLabel l r)


data L1 = L1
data L2 = L2

e = emptyRecord
f = L1 .=. True .*. emptyRecord

test1 = getOptional L1 f
test2 = getOptional L1 e
test3 = getOptional L2 f
-- test4 :: Maybe () -- this would be a type error
-- test4 = getOptional L1 f

main = print ( test1 -- inferred becuase it is Just {}
             , test2 :: Maybe () -- must specify for Nothing
             , test3 :: Maybe () -- must specify for Nothing
             )

我还使用“更高级别”的HList谓词包括下面的第二个实现。这将删除GetOptional类型类,并使g​​etOptional成为一个简单的函数:

{{1}}

编辑:这是Maybe版本,需要所有Nothing答案的类型注释:

{{1}}