如何使用长参数列表做无点样式

时间:2014-02-07 20:04:06

标签: scala haskell f# pointfree

我有一个创建Async工作流的函数,以及以咖喱风格获取10个参数的函数。 e.g。

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#,我真正想要的只是如何做这种currying,这适用于任何;我认为Haskell或Scala的答案很容易移植到F#,很可能被标记为正确的答案。)

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


更新 geesh我不会给出一个与问题争论的答案,而不是回答100分,即使它是最高的投票,所以在这里:

我有一个创建Async工作流的函数,以及以咖喱风格获取 4 参数的函数。 e.g。

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

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

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

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

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

4 个答案:

答案 0 :(得分:9)

10个参数听起来太多了......你怎么用10个属性来创建一个记录,或者在每个情况下你不需要10个属性的DU?无论哪种方式,你最终会得到一个单一的参数,正常的函数组合再次按预期工作。

编辑:实际需要时,您可以创建更强大的<<>>运营商版本:

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

答案 1 :(得分:6)

正如@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

答案 2 :(得分:2)

您可以将参数设置为createSequenceCore

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

let startSequenceCore =
  createSequenceCore >> Async.StartImmediate

答案 3 :(得分:1)

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

只需编写自己的composeN函数。

let compose4 g f x0 x1 x2 x4  = 
    g (f x0 x1 x2 x4)

let startSequenceCore =
    compose4 Async.StartImmediate createSequenceCore