How to do pointfree style with long parameter list

烂漫一生 提交于 2019-12-13 13:02:55

问题


I've got a function that creates an Async workflow, and the function that takes 10 arguments in curry style. e.g.

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

I want to create another function to start that workflow, so I've got

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)

Is there any way I can get rid of those redundant parameters? I tried the << operator, but that only lets me remove one.

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

(I added Haskell and Scala to this question even though the code itself is F#, as really what I want is just how to do this kind of currying, which would apply to any; I'd think a Haskell or Scala answer would be easily portable to F# and could well be marked as the correct answer).

NOTE Reasonably well showing that there is not an easy solution to this could also get the bounty.


UPDATE geesh I'm not going to give 100 points to an answer that argues with the question rather than answering it, even if it's the highest voted, so here:

I've got a function that creates an Async workflow, and the function that takes 4 arguments in curry style. e.g.

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

I want to create another function to start that workflow, so I've got

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

Is there any way I can get rid of those redundant parameters? I tried the << operator, but that only lets me remove one.

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

回答1:


10 arguments sounds like too many... How about you'd create a record with 10 properties instead, or maybe a DU where you don't need all 10 in every case? Either way, you'd end up with a single argument that way and normal function composition works as expected again.

EDIT: When you actually need it, you can create a more powerful version of the << and >> operators thusly:

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

and then you can just write:

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

or

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

P.S.: The argument f is there, so that the type inference infers generic args as opposed to obj.




回答2:


As already mentioned by @Daniel Fabian, 10 arguments is way too many. In my experience even 5 arguments is too many and the code becomes unreadable and error prone. Having such functions usually signals a bad design. See also Are there guidelines on how many parameters a function should accept?

However, if you insist, it's possible to make it point-free, although I doubt it gains any benefit. I'll give an example in Haskell, but I believe it'd be easy to port to F# as well. The trick is to nest the function composition operator:

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

Replacing f with (f .) lifts a function to work one argument inside, as we can see by adding parentheses to the type of (.):

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

See also this illuminating blog post by Conal Elliott: Semantic editor combinators




回答3:


You could tuple the arguments to createSequenceCore:

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

let startSequenceCore =
  createSequenceCore >> Async.StartImmediate



回答4:


I am assuming you just want to write clean code as opposed to allow currying one parameter at a time.

Just write your own composeN function.

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

let startSequenceCore =
    compose4 Async.StartImmediate createSequenceCore


来源:https://stackoverflow.com/questions/21636772/how-to-do-pointfree-style-with-long-parameter-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!