(不)在元组上使用惰性模式匹配的情况

时间:2015-03-07 06:07:37

标签: haskell pattern-matching

根据我的理解,元组上的延迟模式匹配只是推迟了(,)构造函数的解析......如果我们立即使用这两个字段,那么真的有任何缺点吗?无论如何它都会得到解决......

是否有理由使用延迟模式匹配?

foo ~(x, y) = x + y
-- vs
foo (x, y)  = x + y

您何时更愿意使用第二个?

编辑:我特别感兴趣的是只有一种模式并且模式总是匹配的情况。

4 个答案:

答案 0 :(得分:7)

如果您想在惰性模式中进行任何严格匹配,则不希望使用延迟模式匹配。例如,鉴于这些定义:

foo' (Just x, Just y) = Just (x + y)
foo' _ = Nothing

foo ~(Just x, Just y) = Just (x + y)
foo _ = Nothing

foo' (Just 5, Nothing)给出Nothing,但foo (Just 5, Nothing)会抛出运行时错误 - 另外还会发出编译时警告,_情况是多余的,因为~(…) } case是无可辩驳的。

当你知道模式总是匹配时,使用延迟模式匹配几乎是有意义的,这基本上意味着你只是在整个惰性模式中使用单构造函数类型,例如(,)

答案 1 :(得分:7)

一个例子是State monad。 State Monad的严格/懒惰版本通过它们在绑定期间对(值,状态)对进行模式匹配来区分。

Strict州monad:

m >>= k  = StateT $ \ s -> do
    (a, s') <- runStateT m s
    runStateT (k a) s

Lazy州monad:

m >>= k  = StateT $ \ s -> do
    ~(a, s') <- runStateT m s
    runStateT (k a) s'

懒惰状态monad中的惰性模式匹配允许您定义像这样的奇怪程序(取自Melding Monads的this blog post):

pro :: State [Bool] ()
pro = do
   pro
   s <- get
   put (True : s)

程序使用“头递归”生成True值的惰性无限列表。使用严格状态monad的相同程序会在生成任何内容之前将堆栈清空。

答案 2 :(得分:1)

懒惰有性能成本。有时,它具有性能优势。有严格的State monad danidiaz提及存在的原因;延迟和延迟意味着最终可能会耗尽内存,或者导致缓存性能下降。你必须考虑每种情况下发生的事情。一个好的经验法则是模式匹配应该是严格的,除非有特定的理由让它们变得懒惰。如果有疑问,或者性能至关重要,请尝试使用两种方式进行基准测试和/或分析。

答案 3 :(得分:0)

需要~的示例:由于懒惰,您可以折叠这样的无限结构以获得第一个元素:

foldMap (First . Just) [(1::Int)..]

但是,如果您想使用Monoid (a, b)实例一次计算两个此类结果,则会失败:

foldMap (\x -> (First (Just x), First (Just x))) [(1::Int)..]

永远不会结束。原因是mappend的{​​{1}}定义中的非延迟模式匹配:

(,)

但是,使用延迟匹配定义相同的monoid

 instance (Monoid a, Monoid b) => Monoid (a,b) where
    mempty = (mempty, mempty)
    (a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)

按预期工作:

newtype LazyTuple a b = LazyTuple (a, b)
  deriving (Eq, Ord, Show, Read)

instance (Monoid a, Monoid b) => Monoid (LazyTuple a b) where
    mempty = LazyTuple (mempty, mempty)
    LazyTuple (~(x1, y1)) `mappend` LazyTuple (~(x2, y2))
      = LazyTuple (x1 <> x2, y1 <> y2)

立即结束。