Function composition

Hi, I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool But not: *Main> let isNotEqual = not . (==) <interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==) Why? Oscar

2008/12/26 Oscar Picasso
Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
The definition of (.): f . g = \x -> f (g x) So: not . (==) = \x -> not ((==) x) But (==) x is a function (of type a -> Bool, which returns whether its argument is equal to x), not a Bool as not is expecting. Composition like that usually only works for single argument functions. It gets uglier quickly for multi arg functions: isNotEqual = (not .) . (==) You can define your own operator to help: (.:) = (.) . (.) Which is a bit of silly definition. I typically don't like pointfree style for any but the most transparent of uses, so in real life (tm), I'd probably write: (f .: g) x y = f (g x y) But be prepared for others to disagree with me. Luke
<interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==)
Why?
Oscar
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Luke Palmer schrieb:
2008/12/26 Oscar Picasso
mailto:oscarpicasso@gmail.com> Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
The definition of (.):
f . g = \x -> f (g x)
http://www.haskell.org/haskellwiki/Composing_functions_with_multiple_values

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Check the type of (.) Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c Then the type of (.) not Prelude> :t (.) not (.) not :: (a -> Bool) -> a -> Bool Now try to apply (==) Prelude> :t (.) not (==) -- not going to happen Won't happen. What do you want to happen? Oscar Picasso wrote:
Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
<interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==)
Why?
Oscar
----------------------------------------------------------------------
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
- -- Tony Morris http://tmorris.net/ S, K and I ought to be enough for anybody. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFJVamwmnpgrYe6r60RAnerAKDAs4KKsGHN7/WnqUYAJcVJixQiCgCgkQTV CgeAJDTFEeKdAl4Ep3ibG88= =GcLE -----END PGP SIGNATURE-----

* Oscar Picasso
Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
<interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==)
Why?
You might want to read about currying[1]. This will explain why (==) does not take a pair of values, it rather takes one value and then another, and that's why it is not composable in the way you want. What you're trying to do is easier to do with uncurried functions: Prelude> let isNotEqual = not . uncurry (==) Prelude> :t isNotEqual isNotEqual :: (Eq a) => (a, a) -> Bool Prelude> isNotEqual (3,4) True Prelude> isNotEqual (3,3) False (note that -XNoMonomorphismRestriction is used here) 1. http://www.haskell.org/haskellwiki/Currying -- Roman I. Cheplyaka :: http://ro-che.info/ "Don't let school get in the way of your education." - Mark Twain

* Roman Cheplyaka
* Oscar Picasso
[2008-12-26 22:37:26-0500] Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
<interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==)
Why?
You might want to read about currying[1]. This will explain why (==) does not take a pair of values, it rather takes one value and then another, and that's why it is not composable in the way you want.
What you're trying to do is easier to do with uncurried functions:
Prelude> let isNotEqual = not . uncurry (==) Prelude> :t isNotEqual isNotEqual :: (Eq a) => (a, a) -> Bool Prelude> isNotEqual (3,4) True Prelude> isNotEqual (3,3) False
(note that -XNoMonomorphismRestriction is used here)
... and isNotEqual = curry $ not . uncurry (==) is curried version, which has type isNotEqual :: (Eq b) => b -> b -> Bool -- Roman I. Cheplyaka :: http://ro-che.info/ "Don't let school get in the way of your education." - Mark Twain

Hi Oscar,
Define
result = (.)
Then '(result f) g' applies f to the result part of a function g. That's
what you want when negating the result of not. For (==), you want to negate
the result of the result, so you'd instead say '(result.result) not (==)'.
Keep composing result for deeper applications.
If you want to edit/transform the first or second part of a pair instead of
the result of a function, then you'd use 'first' or 'second' in place of
'result'.
If you think of f (here f=not) as an "semantic editor" (transformer) of
values, then 'result', 'first', and 'second' are "semantic editor
combinators", which can be composed together arbitrarily. See
http://conal.net/blog/semantic-editor-combinators .
- Conal
2008/12/26 Oscar Picasso
Hi,
I can write: *Main> let yes = not . not *Main> :t yes yes :: Bool -> Bool
But not: *Main> let isNotEqual = not . (==)
<interactive>:1:23: Couldn't match expected type `Bool' against inferred type `a -> Bool' Probable cause: `==' is applied to too few arguments In the second argument of `(.)', namely `(==)' In the expression: not . (==)
Why?
Oscar

Conal Elliott wrote:
If you think of f (here f=not) as an "semantic editor" (transformer) of values, then 'result', 'first', and 'second' are "semantic editor combinators", which can be composed together arbitrarily. See http://conal.net/blog/semantic-editor-combinators .
I recently found out Henning and Luke's Data.Accessor[1], and your editor combinators and their accessors seem very much related. Was one inspired by the other? Martijn. [1] http://hackage.haskell.org/cgi-bin/hackage-scripts/package/data-accessor

I hadn't seen data-accessor before. Thanks for the pointer. - Conal On Mon, Dec 29, 2008 at 1:34 PM, Martijn van Steenbergen < martijn@van.steenbergen.nl> wrote:
Conal Elliott wrote:
If you think of f (here f=not) as an "semantic editor" (transformer) of values, then 'result', 'first', and 'second' are "semantic editor combinators", which can be composed together arbitrarily. See http://conal.net/blog/semantic-editor-combinators .
I recently found out Henning and Luke's Data.Accessor[1], and your editor combinators and their accessors seem very much related. Was one inspired by the other?
Martijn.
[1] http://hackage.haskell.org/cgi-bin/hackage-scripts/package/data-accessor

On Mon, 29 Dec 2008, Martijn van Steenbergen wrote:
Conal Elliott wrote:
If you think of f (here f=not) as an "semantic editor" (transformer) of values, then 'result', 'first', and 'second' are "semantic editor combinators", which can be composed together arbitrarily. See http://conal.net/blog/semantic-editor-combinators .
I recently found out Henning and Luke's Data.Accessor[1], and your editor combinators and their accessors seem very much related. Was one inspired by the other?
Martijn.
Well spotted - thank you for pointing out this connection! Interestingly since Conal's approach is motivated differently, he also included functions. Those are missing in data-accessor, although Accessor.Container is close to it. The main difference is, that Conal restricts to 'modify' functions, and does not support 'get' like Accessors do. You can 'set' and 'modify' all function values (or all container elements) simultaneously, but you can only 'get' one at a time. Thus an Accessor could only access a single function value, but not all of them simultaneously.
participants (8)
-
Conal Elliott
-
Henning Thielemann
-
Henning Thielemann
-
Luke Palmer
-
Martijn van Steenbergen
-
Oscar Picasso
-
Roman Cheplyaka
-
Tony Morris