Haskell:HList 和可选参数

标签 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在调用代码中。

最佳答案

定义 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来?选择()还是空?离开函数依赖使用户在某处提供类型注释。

也许更好的方法是提供一个默认值(比如 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

编辑:这是可能的版本,它需要所有无答案的类型注释:
{-# 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
             )

关于Haskell:HList 和可选参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12065246/

相关文章:

scala - 如何从 Scala HList 中读取元素?

haskell - 异构列表的笛卡尔积

haskell - 如何调用 “--enable-stdcall-fixup” 选项?

haskell - Haskell 终端输入中的可编辑默认字符串

haskell - 如何将类型族命名为高阶类函数

haskell - 修复 HList 中的类型推断

scala - 找不到参数 tupler 的隐式值

linux - gcc 显示 "Need native word sized stores/loads for atomicity"用于调用 `hlist_nulls_add_head_rcu`

haskell - 在haskell中是否有比较/更改一条/两条记录的规范方法?

haskell - 如何在使用 GHC 编译的 Haskell 函数中找到分配?