如何在 HList 上编写异构列表?
How to write a Heterogeneous list on HList?
我想使用 HList: Heterogeneous lists.
安装库然后 import Data.HList (HList)
到目前为止已经完成。
现在调查 https://bitbucket.org/HList/hlist/src/master/examples/HListExample/ 并不能帮助我开始编写代码。
我想做的是,
创建一个 hello-world 异构列表,例如 H[1, "2"]
(完全不确定语法)
将异构列表映射到每个print
。
有没有人熟悉如何使用HList
?
您可以使用hEnd
和hBuild
函数构造一个HList,例如:
hello = hEnd (hBuild 1 "2")
在此列表上映射 print
要困难得多。我认为没有简单的方法来编写能够映射 print
到 HList 的高阶函数,但是您可以像这样手动遍历 HList:
{-# LANGUAGE DataKinds #-}
import Data.HList
class PrintEach ts where
printEach :: HList ts -> IO ()
instance PrintEach '[] where
printEach HNil = pure ()
instance (Show t, PrintEach ts) => PrintEach (t : ts) where
printEach (HCons x xs) = print x *> printEach xs
hello = hEnd (hBuild 1 "2")
main = printEach hello
-- This prints:
-- 1
-- "2"
您可能不希望只映射 HList,而是遍历,又名mapM
. And you're probably not interested in the individial result-()
s but only the side effects, so that would be mapM_
那么
HList 确实有一个通用的工具,毫不奇怪地称为 hMapM_
。将它与任意 constrained-polymorphic 函数一起使用有点复杂,但对于具体打印来说,这实际上是 ready-for-use 随库一起提供的:
Prelude Data.HList> let l = 1.*."2".*.HNil :: HList '[Int, String]
Prelude Data.HList> l
H[1,"2"]
Prelude Data.HList> hMapM_ HPrint l
1
"2"
正如 Noughtmare 评论的那样,将其与其他多态函数一起使用(无需编写自定义类型)的方法是使用 Fun
,如
Prelude Data.HList> hMapM_ (Fun print :: Fun Show (IO ())) l
1
"2"
...这不是 太 尴尬,但仍然有点烦人。我将定义一个同义词,允许使用 TypeApplications 编写它以避免冗余:
{-# LANGUAGE TypeFamilies, RankNTypes, ConstraintKinds, UnicodeSyntax #-}
import Data.Kind
fun :: ∀ (cxt :: Type -> Constraint) getb
. (∀ a. (cxt a) => a -> getb) -> Fun cxt getb
fun = Fun
然后
Prelude Data.HList Data.Kind> :set -XTypeApplications
Prelude Data.HList Data.Kind> hMapM_ (fun @Show print) l
1
"2"
或者我们甚至可以做到
{-# LANGUAGE FlexibleContexts, AllowAmbiguousTypes #-}
h'MapM_ :: ∀ (cxt :: Type -> Constraint) l m
. (Monad m, HFoldr (Mapcar (Fun cxt (m ()))) [m ()] l [m ()])
=> (∀ a. (cxt a) => a -> m ()) -> HList l -> m ()
h'MapM_ f = hMapM_ (Fun f :: Fun cxt (m ()))
然后
Prelude Data.HList Data.Kind> h'MapM_ @Show print l
1
"2"
我想使用 HList: Heterogeneous lists.
安装库然后 import Data.HList (HList)
到目前为止已经完成。
现在调查 https://bitbucket.org/HList/hlist/src/master/examples/HListExample/ 并不能帮助我开始编写代码。
我想做的是,
创建一个 hello-world 异构列表,例如
H[1, "2"]
(完全不确定语法)将异构列表映射到每个
print
。
有没有人熟悉如何使用HList
?
您可以使用hEnd
和hBuild
函数构造一个HList,例如:
hello = hEnd (hBuild 1 "2")
在此列表上映射 print
要困难得多。我认为没有简单的方法来编写能够映射 print
到 HList 的高阶函数,但是您可以像这样手动遍历 HList:
{-# LANGUAGE DataKinds #-}
import Data.HList
class PrintEach ts where
printEach :: HList ts -> IO ()
instance PrintEach '[] where
printEach HNil = pure ()
instance (Show t, PrintEach ts) => PrintEach (t : ts) where
printEach (HCons x xs) = print x *> printEach xs
hello = hEnd (hBuild 1 "2")
main = printEach hello
-- This prints:
-- 1
-- "2"
您可能不希望只映射 HList,而是遍历,又名mapM
. And you're probably not interested in the individial result-()
s but only the side effects, so that would be mapM_
那么
HList 确实有一个通用的工具,毫不奇怪地称为 hMapM_
。将它与任意 constrained-polymorphic 函数一起使用有点复杂,但对于具体打印来说,这实际上是 ready-for-use 随库一起提供的:
Prelude Data.HList> let l = 1.*."2".*.HNil :: HList '[Int, String]
Prelude Data.HList> l
H[1,"2"]
Prelude Data.HList> hMapM_ HPrint l
1
"2"
正如 Noughtmare 评论的那样,将其与其他多态函数一起使用(无需编写自定义类型)的方法是使用 Fun
,如
Prelude Data.HList> hMapM_ (Fun print :: Fun Show (IO ())) l
1
"2"
...这不是 太 尴尬,但仍然有点烦人。我将定义一个同义词,允许使用 TypeApplications 编写它以避免冗余:
{-# LANGUAGE TypeFamilies, RankNTypes, ConstraintKinds, UnicodeSyntax #-}
import Data.Kind
fun :: ∀ (cxt :: Type -> Constraint) getb
. (∀ a. (cxt a) => a -> getb) -> Fun cxt getb
fun = Fun
然后
Prelude Data.HList Data.Kind> :set -XTypeApplications
Prelude Data.HList Data.Kind> hMapM_ (fun @Show print) l
1
"2"
或者我们甚至可以做到
{-# LANGUAGE FlexibleContexts, AllowAmbiguousTypes #-}
h'MapM_ :: ∀ (cxt :: Type -> Constraint) l m
. (Monad m, HFoldr (Mapcar (Fun cxt (m ()))) [m ()] l [m ()])
=> (∀ a. (cxt a) => a -> m ()) -> HList l -> m ()
h'MapM_ f = hMapM_ (Fun f :: Fun cxt (m ()))
然后
Prelude Data.HList Data.Kind> h'MapM_ @Show print l
1
"2"