【问题标题】:Haskell: HList and optional parametersHaskell:HList 和可选参数
【发布时间】:2012-08-22 01:30:51
【问题描述】:

我一直在尝试使用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

【问题讨论】:

    标签: haskell hlist


    【解决方案1】:

    定义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可以通过在r中查找l来确定,但如果l不在r中,那么v应该从哪里来?选择()还是空?去掉函数依赖会让用户在某处提供类型注释。

    也许更好的方法是提供一个默认值(比如 fromMaybe):

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

    更复杂的版本可能会提供一个函数来使用现有值 (v->w) 和默认值 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
    

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

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

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

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

    【讨论】:

    • 谢谢克里斯,这看起来不错!一个后续问题,您是否可以添加另一个响应以使默认值Nothing 和返回值Just v?大部分参数没有默认值,如果为空,则为空(与空白不同)。
    • 将 (default :: v) 替换为 (Nothing :: forall a . Maybe a) 就像您之前的 Empty 一样。它对类型推断没有帮助。
    • 我添加了 Maybe 版本,并带有类型注释以使其工作。
    • 我知道我在这里问了很多,但你介意举个例子,类似的东西,但在这里使用 Records 包:hackage.haskell.org/package/records-0.1.1.6。我会创建一个赏金并奖励给你,因为这一切对我来说真的很有帮助(希望对其他人有帮助)。
    • 在我的代码示例末尾有一个示例:eftest1,2,3
    猜你喜欢
    • 2013-02-14
    • 2019-12-15
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 2022-07-24
    • 1970-01-01
    • 2012-06-19
    • 2012-12-02
    相关资源
    最近更新 更多