tfausak / flow

:droplet: Write more understandable Haskell.
https://hackage.haskell.org/package/flow
MIT License
198 stars 10 forks source link

Missing (||> and |||>) #18

Closed Swoorup closed 4 years ago

Swoorup commented 4 years ago

This library appears to be missing those 2 operators.

val ( ||> ): 
   arg1: 'T1  *
   arg2: 'T2  *
   func: 'T1 -> 'T2 -> 'U 
      -> 'U

val ( |||> ): 
   arg1: 'T1  *
   arg2: 'T2  *
   arg3: 'T3 ->
   func: 'T1 -> 'T2 -> 'T3 -> 'U 
      -> 'U
tfausak commented 4 years ago

I was not familiar with these operators. I thought this might be similar to #13. After some searching, I found then in the F# documentation. Rewriting the types in Haskell, I think they would be:

(||>) ::
  (a, b) ->
  (a -> b -> c) ->
  c

(|||>) ::
  (a, b, c) ->
  (a -> b -> c -> d) ->
  d

I'll focus on (||>) since (|||>) is more or less the same. (Also it raises the question: When do you stop? Is (||||>) necessary?) I think you can get the same behavior with uncurry:

("hello", "world") |> uncurry (++)
-- "helloworld"

Is this operator used enough to justify it's existence? I'm not sure. What do you think?

In the meantime, you could define this yourself like so:

(||>) = flip uncurry
Swoorup commented 4 years ago

It is useful for passing tuplelized arguments to curried functions. I don’t think F# defined more than 3

Swoorup commented 4 years ago

Example

-- Haskell with flow

taxOf :: Decimal -> [(Decimal, Decimal)] -> Decimal
taxOf salary taxRates = 
    ((0, 0) : taxRates, taxRates)
    ||> zip
    |> map \((_, prevBand), (rate, band)) -> (prevBand, rate, band))
    |> sumBy \(prevBand, rate, band) ->  
        case salary of 
            x | x < prevBand -> 0
            x | x > band -> (band - prevBand) * rate
            x -> (x - prevBand) * rate
tfausak commented 4 years ago

Thanks for providing an example!

In this particular case I would probably rewrite the function to use uncurry directly or call zip with the arguments directly.

taxOf salary taxRates =
  zip ((0, 0) : taxRates) taxRates
  |> ...
taxOf salary taxRates =
  ((0, 0) : taxRates, taxRates)
  |> uncurry zip
  |> ...

In general I'd prefer to have fewer operators. It's usually possible to use other combinators (like uncurry) to achieve the same thing with a smaller vocabulary.

One reason I'm opposed to more operators (other than the "where to stop" question I already mentioned) is that I'd want to add (<||), (..>), (<..), (!!>), and (<!!) for consistency. That's a lot to add!

So for now I'm leaning toward keeping Flow as it is. But I think it's good to consider things like this! Thanks for bringing it up!

Swoorup commented 4 years ago

In F#, there are only the following, the other operators you've mentioned aren't there.

No biggie though eitherways