【问题标题】:How to do pointfree style with long parameter list如何用长参数列表做无点样式
【发布时间】:2015-06-20 05:31:51
【问题描述】:

我有一个创建异步工作流的函数,以及接受 10 个 curry 风格参数的函数。例如

let createSequenceCore a b c d e f g h i j = 
  async { 
    ... 
  }

我想创建另一个函数来启动该工作流,所以我有

let startSequenceCore a b c d e f g h i j =
  Async.StartImmediate (createSequenceCore a b c d e f g h i j)

有什么办法可以去掉那些多余的参数吗?我尝试了<< 运算符,但这只能让我删除一个。

let startSequenceCore a b c d e f g h i =
  Async.StartImmediate << (createSequenceCore a b c d e f g h i)

(我在这个问题中添加了 Haskell 和 Scala,即使代码本身是 F#,因为我真正想要的是如何进行这种柯里化,这适用于任何问题;我认为 Haskell 或 Scala 答案将很容易移植到 F# 并且很可能被标记为正确答案)。

注意合理地表明没有简单的解决方案也可以获得赏金。


更新天哪,我不会给与问题争论的答案打 100 分,而不是回答它,即使它是最高票数,所以在这里:

我有一个创建异步工作流的函数,以及接受 4 咖喱风格参数的函数。例如

let createSequenceCore a b c d = 
  async { 
    ... 
  }

我想创建另一个函数来启动该工作流,所以我有

let startSequenceCore a b c d =
  Async.StartImmediate (createSequenceCore a b c d)

有什么办法可以去掉那些多余的参数吗?我尝试了&lt;&lt; 运算符,但这只能让我删除一个。

let startSequenceCore a b c =
  Async.StartImmediate << (createSequenceCore a b c)

【问题讨论】:

  • 添加了更完整的答案作为编辑,请查看。

标签: scala haskell f# pointfree


【解决方案1】:

10 个参数听起来太多了...不如创建一个包含 10 个属性的记录,或者创建一个不需要所有 10 个属性的 DU?无论哪种方式,您最终都会得到一个参数,并且正常的函数组合再次按预期工作。

编辑:当您真正需要它时,您可以创建更强大的 &lt;&lt;&gt;&gt; 运算符版本,从而:

let (<.<) f = (<<) (<<) (<<) f
let (<..<) f = (<<) (<<) (<.<) f
let (<...<) f = (<<) (<<) (<..<) f

let flip f a b = f b a
let (>.>) f = flip (<.<) f
let (>..>) f = flip (<..<) f
let (>...>) f = flip (<...<) f

然后你就可以写了:

let startSequenceCore =
    Async.StartImmediate <...< createSequenceCore

let startSequenceCore =
    createSequenceCore >...> Async.StartImmediate

P.S.:参数 f 在那里,因此类型推断推断通用 args 而不是 obj

【讨论】:

    【解决方案2】:

    正如@Daniel Fabian 已经提到的,10 个参数太多了。根据我的经验,即使是 5 个参数也太多了,代码变得不可读且容易出错。拥有这样的功能通常预示着糟糕的设计。另见Are there guidelines on how many parameters a function should accept?

    但是,如果您坚持,可以让它无意义,尽管我怀疑它会带来任何好处。我将在 Haskell 中举一个例子,但我相信移植到 F# 也很容易。诀窍是嵌套函数组合运算符:

    data Test = Test
      deriving (Show)
    
    createSequenceCore :: Int -> Int -> Int -> Int -> Int
                       -> Int -> Int -> Int -> Int -> Int -> Test
    createSequenceCore a b c d e f g h i j = Test
    
    -- the original version
    startSequenceCore :: Int -> Int -> Int -> Int -> Int
                      -> Int -> Int -> Int -> Int -> Int -> IO ()
    startSequenceCore a b c d e f g h i j =
      print (createSequenceCore a b c d e f g h i j)
    
    -- and point-free:
    startSequenceCore' :: Int -> Int -> Int -> Int -> Int
                       -> Int -> Int -> Int -> Int -> Int -> IO ()
    startSequenceCore' =
      (((((((((print .) .) .) .) .) .) .) .) .) . createSequenceCore
    

    f 替换为(f .) 会提升一个函数以处理其中的一个参数,正如我们可以通过在(.) 的类型中添加括号来看到的那样:

    (.) :: (b -> c) -> ((a -> b) -> (a -> c))
    

    另请参阅 Conal Elliott 的这篇富有启发性的博文:Semantic editor combinators

    【讨论】:

    【解决方案3】:

    您可以将参数元组化为createSequenceCore

    let createSequenceCore(a, b, c, d, e, f, g, h, i, j) = 
      async { 
        ... 
      }
    
    let startSequenceCore =
      createSequenceCore >> Async.StartImmediate
    

    【讨论】:

    • 我对@9​​87654323@ 中的函数进行了重新排序,以便可以像使用|&gt; 运算符一样从左到右读取它。
    【解决方案4】:

    我假设您只想编写干净的代码,而不是允许一次柯里化一个参数。

    只需编写自己的 composeN 函数即可。

    let compose4 g f x0 x1 x2 x4  = 
        g (f x0 x1 x2 x4)
    
    let startSequenceCore =
        compose4 Async.StartImmediate createSequenceCore
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      • 2014-04-06
      • 2019-08-10
      相关资源
      最近更新 更多