I am finding it hard to find examples of on
in Haskell and I don't understand the explanation on Hoogle's Data.Function
page. Please, can I have examples of its use and examples of problems/code where using it makes a solution simpler or more efficient?
What does Haskell's Data.Function.on do?
189 views Asked by qwertyuiop AtThere are 3 answers
The type signature for on
is
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
And, really, there's only one reasonable (non-bottom) implementation of this function.
f `on` g = \x y -> f (g x) (g y)
Or, written with an infix argument as they do in the GHC source,
(.+.) `on` g = \x y -> g x .+. g y
So the body of the function is fairly simple. The question is: Why would we care.
The most common use case for on
, in my experience, is when augmenting sorting or comparing functions with compare
.
In Python, if we wanted to sort a list of people based on their names, we might write
my_list.sort(key=lambda person: person.name)
Haskell's sortBy
takes a comparison function to sort by, so we would write the same in Haskell as
sortBy (\x y -> getName x `compare` getName y) myList
But that's just on
. So really it's
sortBy (compare `on` getName) myList
"Sort by comparison on name". It actually reads almost like English.
Now, for sorting in particular, this paradigm is so common that more recent versions of Haskell provide sortOn
, which takes a Python-style key argument
sortOn getName myList
But there's a whole collection of "By" functions that work on lists, and the Haskell devs didn't write "On" variants for each of them.
Some examples:
-- Remove entries which have the same name as someone else
nubBy ((==) `on` getName) someList
-- Insert into a list sorted by salary, preserving the ordering
insertBy (compare `on` getSalary) newEmployee employees
-- Find the student with the highest grade
maximumBy (compare `on` getGrade) myStudents
So, generally, on
will be used with a function whose name ends in "By".
Complementing the existing answers, from the viewpoint of some theoreticians, Haskell's Data.Function.on
is an implementation of what some call the psi combinator. Quoting Smullyan†:
A psi bird is a bird Ψ satisfying the condition Ψxyzw = x(yz)(yw)
† Raymond M. Smullyan, To mock a mockingbird, Oxford University Press, 2000.
Related
Suppose we have a function
f
that takes two arguments. We want a similar function which acts asf
, but only after both its arguments have been modified by some other functiong
.We could write a new definition
or, exploiting
on
,Nicely, the latter can be used directly, without defining a new symbol
new_f
. (Using a lambda is also viable, but not as nice)This is typically used with
compare
, a library binary function that checks the ordering between its two arguments. So, we can useto sort according different criteria. Above, we assume that
name
, etc. are functions that can extract the relevant information from a person.(In more modern Haskell we also have
sortBy (comparing age) somePeopleList
, or evensortOn age somePeopleList
for the same task)