【发布时间】:2015-04-07 09:05:39
【问题描述】:
考虑一下可爱的小 HoleyMonoid 库,它可以让您构建类型化的可变参数 printf 类函数,如下所示:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Category
import Data.HoleyMonoid
import Prelude hiding ((.), id)
foo =
now "hello "
. later id
. now ", you are "
. later show
. now " years old"
bar = run foo
-- > bar "alice" 42
-- "hello alice, you are 42 years old"
-- > bar 42 "alice"
-- type error
有没有办法检查容器(列表、AST 等)并根据其内容构建这样的函数?
作为一个玩具示例,您可以画出如下图:
import Data.Monoid
adder = go where
go [] = now (Sum 0)
go (x:xs)
| x == 5 = now 100 . go xs
| otherwise = later id . go xs
-- hypothetical usage
--
-- > :t run adder [1, 3, 5]
-- Num a => Sum a -> Sum a -> Sum a
--
-- > getSum $ run adder [1, 3, 5] 0 1
-- 101
adder 未通过发生检查,但您可以看到我的目标。问题似乎是很难在任何地方保持计算状态,因为 now 100 和 later id 属于不同的类型。
【问题讨论】:
-
好吧,
adder应该是什么类型?显然您希望类型取决于列表的长度。那将是dependent type。 Haskell 不是 (quite) 依赖类型语言,所以这是不可能的。可行的方法是根据您提供的参数来决定类型,并在与列表不匹配时在运行时崩溃……但这不完全是 Haskell 惯用的。 -
不适用于常规列表。使用 GADT-y 列表,您可能会实现类似的目标,例如如stackoverflow.com/a/25422805/3234959
-
@leftaroundabout 这可能不是惯用的,但这基本上就是
Text.Printf所做的。除了运行时检查之外,该方法还存在无法处理像show这样的多态函数的问题。 -
嗯,printf 有一个特殊的应用程序,其中这种特殊的重载机制恰到好处——如果你想在一行中快速打印多个原始(单态)值,可能是不同的类型。如果不是针对不同类型的需求,最好用一个简单的列表参数来实现。
-
目前在我的实际应用程序中,我将标记为
Dynamic参数的Map传递给函数,其中标签指示向AST 的哪个节点提供值。这可行,但显然不理想;特别是通过toDyn手动包装参数是一种痛苦。我不能 100% 确定printf风格的路线是否真的更好,但它可能是。
标签: haskell variadic-functions continuations