【问题标题】:is there any syntax for non-recursive binding in Haskell, just like the difference between `let` and `let rec` in similar languages?Haskell中是否有任何非递归绑定的语法,就像类似语言中的`let`和`let rec`之间的区别?
【发布时间】:2021-07-19 07:26:15
【问题描述】:

非递归绑定允许我隐藏绑定值,例如:

b a = let norec a = a + 10 in a

这里我自己创建的let norec 表示let 绑定但不是递归的。

这在使用记录通配符时非常有用:

data MyRecord = MyRecord{ {- vary huuuuuge set of definitions -} }

foo MyRecord{..} = let norec field1 = field1 + 1
                             field2 = modify field2
                             {- some other modifications to the fields -}
                    in MyRecord{..}

这可以实现吗?或者在您的情况下如何处理?

【问题讨论】:

    标签: haskell


    【解决方案1】:

    记录通配符在这里真的有用吗?对我来说,通常的旧做事方式看起来很简洁:

    foo r = r { field1 = field1 r + 1, field2 = modify (field2 r) }
    

    您的问题的直接答案是 Haskell 中没有 let 的非递归模拟;虽然你可以使用 Identity monad 来破解一些东西:

    foo MyRecord{..} = runIdentity $ do
        field1 <- return (field1 + 1)
        field2 <- return (modify field2)
        return MyRecord{..}
    

    【讨论】:

      【解决方案2】:

      Haskell 中是否有非递归绑定的语法

      不,在定义具有相同名称的新变量时,无法引用先前定义的给定名称的变量。

      foo MyRecord{..} = let norec field1 = field1 + 1
                                   field2 = modify field2
                                   {- some other modifications to the fields -}
                          in MyRecord{..}
      

      这可以通过明确拼写修改的字段来实现(同时仍然用通配符覆盖未更改的字段)。由于您不需要这种方式的局部变量,因此它甚至不需要更多代码:

      foo MyRecord{..} =
        MyRecord {
          field1 = field1 + 1,
          field2 = modify field2,
          ..
        }
      

      请注意,这里等号左边的标识符是记录标签,而不是变量,而右边的标识符是变量,所以尽管这看起来是递归的,但它不是。

      【讨论】:

        【解决方案3】:

        不是您问题的答案,但我想指出lens 替代方案。对于 Haskell 的记录问题,Lenses 确实是一个很好的解决方案,这对于更大的数据定义来说变得相当痛苦,而且 RecordWildcards 确实没有像我们想要的那样很好地解决。

        {-# LANGUAGE TemplateHaskell #-}
        import Control.Lens
        import Control.Lens.TH (makeLenses)
        
        data MyRecord = MyRecord{ _field1 :: ...
                                , _field2 :: ...
                                {- note the underscores -} }
        makeLenses ''MyRecord
        
        foo = (field1 %~ (+1))
            . (field2 %~ modify)
            . ...
        

        【讨论】:

          【解决方案4】:

          一个选项是:

          -- also available from the lens package
          (&) :: a -> (a->b) -> b
          (&) = flip ($)
          
          f a = -- let norec a = a + 1 in a+a
                (a+1) & \a -> a + a
          
          g x = -- let norec x = x+1 in
                (x+1) & \x ->
                -- let norec x = x+x in
                (x+x) & \x -> 
                -- 1000+x
                1000+x
          

          不是很优雅,但至少它很简洁。它还会产生一些“阴影”警告。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-09-23
            • 1970-01-01
            • 2015-10-18
            • 1970-01-01
            • 2013-09-22
            • 2016-02-13
            • 2020-01-25
            • 1970-01-01
            相关资源
            最近更新 更多