문제

I don´t like to read the order of functions from right to left in haskell. To fix this, I added a little useful operator.

module Main where
(>>>)            :: a -> (a -> b) -> b
(>>>) a fun      =  fun a 
main = print $ [1..10] >>> map (*2) >>> filter (>5) >>> foldr1 (+)
-- => 104

I think I could find here a similar built-in operator. Like bind operator (>>=). But bind operator works differently or to be exact I don't understand how it works. It seems it use concat map. But why?

My next point to improve that operator is make him called once. For example:

print $ [1..10] >>> map (*2) $ filter (>5) $ foldr (+)

I try to make it with (>>>) a = a and (>>>) a fun = (>>>) (fun a), but it seems that this overload is not possible. Clarify that I focused on the learning of functions and still don`t know anything about monads, types and classes.

So my question is: built in operator or correct using of bind operator.

도움이 되었습니까?

해결책

"I don´t like to read the order of functions from right to left in haskell. To fix this, I added a little useful operator."

"still don`t know anything about monads, types and classes"

I don't think that trying to "fix" something in the language without understanding its basic concepts is a good idea.

First of all, there are popular libraries and functions in Haskell providing what you want, e.g. lens's & operator does exactly what your >>> does. Secondly, the name >>> is already occupied by a Category's (of the base library) implementation, so it's not a good idea to reimplement it. Basically it is just a reverse of a composition operator ., but I suspect you're not well acquainted with function composition either. Thirdly, the bind (>>=) operator is too far in purposes from what you expect, you need to study monads to understand it.

다른 팁

>>= has the type m a -> (a -> m b) -> m b. In the case where m is a [] (a list), it does behave like concatMap because then the types line up nicely. What you've written is a completely different type so you can't expect them to work the same.

Incidentally, what you've written is already defined exactly that way in Control.Category

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

So if you want to have just good old function combination, use it. It can do other things as well but you don't have to worry about them.

The second code snippet doesn't make much sense. $ is the opposite of >>> so you've just written

(foldr (+) >>> ( filter (>5) >>> ([1..10] >>> map (*2)))) >>> print

or without so many parens

(foldr (+) >>> filter (>5) >>> [1..10] >>> map (*2)) >>> print

which doesn't make sense no matter how you overload things. Instead, using .

[1..10] >>> print . foldr (+) 0 . filter (>5) . map (*2)

and then you can even avoid >>> entirely to give

print . foldr (+) 0 . filter (>5) . map (*2) $ [1..10]

which is quite a common Haskell pattern. In summary, you can't get the "read my mind" operator that you were looking for, but . $, and >>> get you pretty far all on their own.

  1. I think it's called |> in F#:

    main = print $ [1..10] |> map (*2) |> filter (>5) |> foldr1 (+)

    Note that |> is asymmetric in its argument types: it expects a value, and a function, and applies the value to a function: x |> f = f x. It is, of course, just ($) flipped:

    x |> f = f x = f $ x = ($) f x = flip ($) x f.

  2. There's a similar, but different operator defined in Control.Category, (>>>). With it, we can write

    main = print $ [1..10] |> (map (*2) >>> filter (>5) >>> foldr1 (+))

    Note that >>> is symmetric in its argument types: it expects two functions, and produces another function, their left-to-right composition. It is, of course, just (.) flipped: >>> == flip (.), when specialized for functions:

    f . g $ x = f (g x)

    f >>> g $ x = g (f x) = g . f $ x = flip (.) f g x

  3. So we can write in this style with just built-in operators, as

     main = print $ ($ [1..10]) (map (*2) >>> filter (>5) >>> foldr1 (+))

Oh, and you can do this with monadic bind, you just need to decorate your code with stuff from Control.Monad.Identity:

> :m +Control.Monad.Identity
> :m +Control.Arrow
> runIdentity $ Identity 2 >>= return . ((3+) >>> (5*))
25

A case for the traditional composition order.

Some composition chain definitions are naturally read from the left. Consider this:

res = fix ((2:) . minus [3..] . bigUnion . map (\p-> [p*p, p*p+p..]))

The data flow is from the right but "comprehension flow" is from the left. The leftmost operator ((2:)) gets executed first, priming the calculation. Putting it last with the left-to-right composition order would perhaps feel less natural, here:

res = (map (\p-> [p*p, p*p+p..]) >>> bigUnion >>> minus [3..] >>> (2:)) |> fix

To comprehend this last definition we are forced to read it from the right, though it is "written" from the left. In general,

res = fix f     ===   res = f res      -- readable, `f` comes first; obviously
                                       -- `f` produces some part of `res`
                                       -- before actually using it

res = f |> fix  ===   res = res |> f   -- not so much; may create an impression
                                       -- `res` is used prior to being defined

but of course YMMV.

The monadic bind operator >>= always returns something 'wrapped' in a monad, so it probably isn't what you want.

Your >>> operator should work fine with a little adjustment, although it might not make sense to mix it with $ as you are doing there. If anything it's just confusing!

The adjustment you will need is to change the fixity of your operator, and to make it left-associative. This will mean you don't need parentheses when nesting them.

The built-in $ operator, which you seem comfortable with, is defined like this:

($) f a = f a
infixr 0 $

Which means it has the lowest possible operator precedence and it's associativity is to the right, meaning a $ b $ c is the same as a $ (b $ c).

Since your operator is right-to-left, you will want the opposite associativity, which you can define like this:

(>>>) a fun      =  fun a 
infixl 0  >>>

Now, you can get rid of the mixed use of $ and >>> and make your program into a pipeline, without adding any parentheses:

 [1..10] >>> map (*2) >>> filter (>5) >>> foldr (+) 1 >>> print
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top