Syntax and semantics of symbol characters in Haskell - haskell

I'm learning Haskell.
One of my standard techniques when learning a new language is to implement a Hello World genetic algorithm that attempts to generate a string via genetic algorithm techniques that matches some input string.
Due to my inexperience with Haskell (the closest thing I have to compare is Kotlin) I searched for an example code so I could match my existing understanding of basic genetic algorithm to the code and intuit some understanding of Haskell based on my already underway reading/research on the language.
I came across this tutorial: https://www.arcadianvisions.com/blog/2011/haskell-genetic-algorithm-hello-world.html
I transcribed it in atom after setting my environment up, each part I didn't understand I did a quick google, if I didn't understand the syntax/semantics after 15 minutes I would carry on transcribing with the intent to catch up later on those particular parts.
So, I understood most of the code, such as the order of function application, monads (think I'm nearly there with monads anyway), data types, function types, currying, type substitution etc. However, there were several bits of syntax/semantics that I haven't seen in my reading/researching and not sure what they do, but they appear a lot in the code example I linked to above. I'm hoping someone can explain them to me:
(++)
(:)
<$>
<*>
(,)
(x !!)
p#(info#())
I am assuming the () and <> are some special syntax and the stuff inside them is semantic? When I hover over them in atom (I'm using atom-haskell-ghc) I can see the type Functor f => Applicative (f :: * -> *) where <*> :: f (a -> b) -> f a -> f b which kind of looks like a monad, but I don't really understand the weird syntax/semantics on the consuming end. Should I think of these as just another function (with a weird infix alias?).
Here is a specific line exhibiting several of the examples from above:
mate :: RandomGen g => Gene -> Gene -> Rand g Gene
mate gene1 gene2 = (++) <$> flip take gene1 <*> flip drop gene2 <$> pivot
where pivot = getRandomR (0, length gene1 - 1)

A note on operators
In Haskell you can define a function that has as identifier a sequence of symbols wrapped between brackets, like for example (++) or (:) that is an operator that can be used both as a function, like (++) x y, and as an infix operator, like x ++ y. Behind the curtains, the Haskell compiler will convert the infix operator to a function call, so x ++ y is completely equivalent with (++) x y (except for the fact that operators have different precedence rules).
(++)
This is the append function: (++) :: [a] -> [a] -> [a] it takes two lists as input, and constructs a list of the same type of elements that contains the elements of the first list followed by the elements of the second list. For example:
(++) [1, 4, 2, 5] [1, 3, 0, 2] == [1, 4, 2, 5, 1, 3, 0, 2]
(:)
This is a constructor of the list type [a]. It has type (:) :: a -> [a] -> [a]. It takes as input an element and a list of elements (all of the same type), it construct a list that starts with the first element, followed by the elements of the second parameter. For example:
(:) 1 [4, 2, 5] = [1, 4, 2, 5]
(<$>)
You wrote in your question <$>, but as you perhaps figured out, that means that somewhere a function (<$>) :: Functor f => (a -> b) -> f a -> f b is defined.
A Functor is a typeclass. Several types in Haskell are functors. The easiest ones are a list [], and a Maybe. The (<$>) takes as input a function f :: a -> b, and a functor instance, for example a list [a]. It will then convert it to a functor instance [b]. How this is done, depends on how the Functor instance is implemented (the (<$>) for a Maybe has different semantics than one for []).
Although the analogy is not complete, you can see a Functor sometimes as a collection of elements (a Maybe basically is a collection of zero Nothing, or one Just x elements). It will then map the elements the collection contains by channeling these through the function, for example:
(+1) <$> [1, 4, 2, 5] == [2, 5, 3, 6]
(+1) <$> Nothing == Nothing
(+1) <$> (Just 2) == Just 3
(<*>)
This function (<*>) :: Applicative f => f (a -> b) -> f a -> f b is again somewhat hard to understand. It makes use of an Applicative type class.
An type that is an instance of Applicative has to implement two functions: pure :: Applicative a => a -> f a and (<*>) :: Applicative f => f (a -> b) -> f a -> f b (or the programmer can decide to instead implement liftA2, but let us ignore that here).
Again you can see Applicative (at least for popular instances) as a collection (like [] or Maybe). Here we thus take as input such collection of functions (all with type a -> b), and a collection of as. We then "multiply" these, in the sense that for instance for a list:
[f1, f2, ..., fm] <*> [x1, x2, ..., xn]
== [f1 x1, f1 x2, ..., f1 xn,
f2 x1, f2 x2, ..., f2 xn,
...,
fm x1, fm x2, ..., fm xn]
So that means for instance for Maybe that if the left operand is Nothing, or the right operand is Nothing, or both are Nothing, then this results in a Nothing, if both are Justs (so Just f <*> Just x), then we obtain a Just (f x):
Just f <*> Just x == Just (f x)
Just f <*> Nothing == Nothing
Nothing <*> Just x == Nothing
Nothing <*> Nothing == Nothing
(,)
This is the constructor of a 2-tuple: (,) :: a -> b -> (a,b) thus takes as input an a and a b, and it constructs an 2-tuple where the first item is the first parameter, and the second item is the second parameter. For example:
(,) 4 'a' == (4, 'a')
(x !!)
This is a section of an infix operator. You can use an infix operator, and for instance specify the left or the right part. In that case you thus construct a partially applied function. For example:
([1, 4, 2, 5] !!) == (!!) [1, 4, 2, 5]
(!! 2) == flip (!!) 2
So for the latter it thus means that we constructed a function that takes as input a parameter that will be filled in as left operand. So:
(!! 2) [1, 4, 2, 5] == (!!) [1, 4, 2, 5]
The (!!) :: [a] -> Int -> a function takes as input a list and an Int and it returns the element at that index (zero-based indices).
p#(info#())
In contrast to the above, the # is not a function or operator (well these are actually the same), but a keyword.
It is used in pattern matching to obtain a reference to both an pattern and for instance match subpatterns (or obtain references to subpatterns).
For instance, say we want to pattern match a 2-tuple, and we want to have a reference to the entire tuple, and the first element, we can use:
somefunction total#(left, _) = ...
So if we then call somefunction (4, 'a'), then this means that total will hold (4, 'a'), and left will hold 4.

These are mostly all regular functions.
The <> are not special syntax, just part of the function name.
The () are regular parenthesis which group things and define precedence like in most other languages, the important note is that when you want to refer to an operator function (like ++) it must be in parenthesis.
++ is the list concatenation function [1,2] ++ [3,4] = [1,2,3,4], or without using infix notation (++) [1,2] [3,4] = [1,2,3,4]
: is the 'cons' function, it prepends an element to a list 1 : [2, 3, 4] = [1,2,3,4] or (:) 1 [2, 3, 4] = [1,2,3,4] without infix notation.
<$> is an infix operator alias of fmap
<*> is the applicative application function
, is the tuple constructor (,) 1 2 = (1, 2)
!! is the list index function [1,2,3] !! 1 = 2. Note that since these are singly linked lists, indexing is an O(n) operation.
# is used to define an "as pattern". When pattern matching it allows you to give a name to a parameter, while also destructuring it with pattern matching. For example the pattern f (xs#[x1, x2]) matches a two element list, where you can use x1 and x2 to refer to the individual elements and xs to refer to the list as a whole

Related

Haskell: Purpose of the flip function?

I am a bit surprised that this was not asked before. Maybe it is a stupid question.
I know that flip is changing the order of two arguments.
Example:
(-) 5 3
= 5 - 3
= 2
flip (-) 5 3
= 3 - 5
= -2
But why would I need such a function? Why not just change the inputs manually?
Why not just write:
(-) 3 5
= 3 - 5
= -2
One is unlikely to ever use the flip function on a function that is immediately applied to two or more arguments, but flip can be useful in two situations:
If the function is passed higher-order to a different function, one cannot simply reverse the arguments at the call site, since the call site is in another function! For example, these two expressions produce very different results:
ghci> foldl (-) 0 [1, 2, 3, 4]
-10
ghci> foldl (flip (-)) 0 [1, 2, 3, 4]
2
In this case, we cannot swap the arguments of (-) because we do not apply (-) directly; foldl applies it for us. So we can use flip (-) instead of writing out the whole lambda \x y -> y - x.
Additionally, it can be useful to use flip to partially apply a function to its second argument. For example, we could use flip to write a function that builds an infinite list using a builder function that is provided the element’s index in the list:
buildList :: (Integer -> a) -> [a]
buildList = flip map [0..]
ghci> take 10 (buildList (\x -> x * x))
[0,1,4,9,16,25,36,49,64,81]
Perhaps more frequently, this is used when we want to partially apply the second argument of a function that will be used higher-order, like in the first example:
ghci> map (flip map [1, 2, 3]) [(+ 1), (* 2)]
[[2,3,4],[2,4,6]]
Sometimes, instead of using flip in a case like this, people will use infix syntax instead, since operator sections have the unique property that they can supply the first or second argument to a function. Therefore, writing (`f` x) is equivalent to writing flip f x. Personally, I think writing flip directly is usually easier to read, but that’s a matter of taste.
One very useful example of flip usage is sorting in descending order. You can see how it works in ghci:
ghci> import Data.List
ghci> :t sortBy
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
ghci> :t compare
compare :: Ord a => a -> a -> Ordering
ghci> sortBy compare [2,1,3]
[1,2,3]
ghci> sortBy (flip compare) [2,1,3]
[3,2,1]
Sometimes you'll want to use a function by supplying the second parameter but take it's first parameter from somewhere else. For example:
map (flip (-) 5) [1..5]
Though this can also be written as:
map (\x -> x - 5) [1..5]
Another use case is when the second argument is long:
flip (-) 5 $
if odd x
then x + 1
else x
But you can always use a let expression to name the first parameter computation and then not use flip.

Apply two Folds or Getters and only succeed when both succeed

Imagine I have the following list:
lst :: [(Bool, Maybe Integer)]
lst = [(True, Just 3), (True, Nothing), (False, Just 12)]
Using the lens library, I want to extract the elements of the tuples, but I only want it to succeed when the second element is Just. I want some optic, split that works like this:
> lst ^.. folded.split (_1.to not) (_2._Just)
[(False, 3), (True, 12)]
I can implement split myself like this:
split :: Getting (First a) s a -> Getting (First b) s b -> Fold s (a, b)
split a b = folding (\x -> (,) <$> (x ^? a) <*> (x ^? b))
…which seems to work. However, this seems like I must be reinventing the wheel. Is there something already provided by the lens library that accomplishes this in an equally nice way?
The aside combinator takes a Prism that works over the second component of a tuple and returns a Prism that works over the whole tuple:
ghci> lst ^.. folded.aside _Just
[(True,3),(False,12)]
The resulting prism matches when the component is matched, otherwise it fails.
Combining it with to and bimap, we can reproduce your example:
ghci> lst ^.. folded.aside _Just.to (bimap not id)
[(False,3),(True,12)]
To work over the first component, we can use swapped:
ghci> [(Just 3,False)]^..folded.swapped.aside _Just.swapped
[(3,False)]

parenthesis in Haskell functions

I just want to know how do we know which functions need brackets () and which ones do not? For example
replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))
works fine. But
replicate 100 (product (map (*3) (zipWith (max [1,2,3,4,5] [4,5,6,7,8]))))
does not work. It is because I put a set of brackets for zipWith. In this small example, zipWith and max do not have brackets, but replicate, product and map do. In general is there a way to know/figure out which functions need brackets and which ones dont.
Function application is left associative. So, when you write an expression like:
f g h x
it means:
((f g) h) x
And also the type of zipWith provides a clue:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
it says that zipWith has 3 parameters: a function and two lists.
When you write:
zipWith (max [1,2,3,4,5] [4,5,6,7,8])
The interpreter will understand that
max [1,2,3,4,5] [4,5,6,7,8]
will be the first parameter to zipWith, which is type incorrect. Note that zipWith expects a function of two arguments as its first argument and, as pointed out by #Cubic, max [1,2,3,4,5] [4,5,6,7,8] will return the maximum
between these two lists according the usual lexicographic order, which will be of type [a], for some type a which is instance of Ord and Num. Said that, the error become evident since you are trying to pass a value of type
(Num a, Ord a) => [a]
where a value of type
(a -> b -> c)
is expected.
Rodrigo gave the right answer. I'll just add that it is a misconception to think that some functions need parentheses, while others don't.
This is just like in school math:
3 * (4+5)
It is simply not the case that + expressions need parentheses and * expressions don't need them in general.
In Haskell, you can always get away without parentheses at all. Whenever you need to enclose an expression in parentheses, the alternative is to introduce a local name and bind it to that expression, then use the name instead of the expression.
In your example:
replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))
let list1 = product list2
list2 = map thrice list3
thrice x = x*3
list3 = zipWith max [1,2,3,4,5] [4,5,6,7,8]
in replicate 100 list1
In fact, I often write functions top down thus:
foo x y z = result
where
result = ...
...
However, as it was said before, expressions that consist of function applications can also often be written without parentheses by making use of (.) and ($) and in such cases, the top down approach from above may be overly verbose and the following would be much clearer (because there is no noise through newly introduced names):
replicate 100
. product
. map (*3)
$ zipWith max [1..5] [4..8]

Comparing List Elements in Haskell

I'm just learning Haskell and am kind of stuck.
I'd like to compare list elements and measure the difference between them and return the highest one.
Unfortunatly, I do not know how to approach that problem.
For usual, I'd just iterate the list and compare the neighbours but that does not seem to be the way to go in Haskell.
I already tried using map but as I said I do not really know how you can solve that problem.
I'd be thankful for every kind of advice!
Best wishes
Edit: My idea is to first zip all pairs like this pairs a = zip a (tail a). Then I'd like to get all differences (maybe with map?) and then just chose the highest one. I just can't handle the Haskell syntax.
I don't know what you mean by "measure the discrepancy" between list elements, but if you want to calculate the "largest" element in a list, you'd use the built-in maximum function:
maximum :: Ord a => [a] -> a
This function takes a list of values that can be ordered, so all numbers, chars, and strings, among others.
If you want to get the difference between the maximum value and the minimum value, you can use the similar function minimum, then just subtract the two. Sure, there might be a slightly faster solution whereby you only traverse the list once, or you could sort the list then take the first and last elements, but for most cases doing diff xs = maximum xs - minimum xs is plenty fast enough and makes the most sense to someone else.
So what you want to do is compute a difference between successive elements, not calculate the minimum and maximum of each element. You don't need to index directly, but rather use a handy function called zipWith. It takes a binary operation and two lists, and "zips" them together using that binary operation. So something like
zipWith (+) [1, 2, 3] [4, 5, 6] = [1 + 4, 2 + 5, 3 + 6] = [5, 7, 9]
It is rather handy because if one of the lists runs out early, it just stops there. So you could do something like
diff xs = zipWith (-) xs ???
But how do we offset the list by 1? Well, the easy (and safe) way is to use drop 1. You could use tail, but it'll throw an error and crash your program if xs is an empty list, but drop will not
diff xs = zipWith (-) xs $ drop 1 xs
So an example would be
diff [1, 2, 3, 4] = zipWith (-) [1, 2, 3, 4] $ drop 1 [1, 2, 3, 4]
= zipWith (-) [1, 2, 3, 4] [2, 3, 4]
= [1 - 2, 2 - 3, 3 - 4]
= [-1, -1, -1]
This function will return positive and negative values, and we're interested only in the magnitude, so we can then use the abs function:
maxDiff xs = ??? $ map abs $ diff xs
And then using the function I highlighted above:
maxDiff xs = maximum $ map abs $ diff xs
And you're done! If you want to be fancy, you could even write this in point-free notation as
maxDiff = maximum . map abs . diff
Now, this will in fact raise an error on an empty list because maximum [] throws an error, but I'll let you figure out a way to solve that.
As mentioned by bheklilr, maximum is the quick and easy solution.
If you want some of the background though, here's a bit. What we're trying to do is take a list of values and reduce it to a single value. This is known as a fold, and is possible with (among others) the foldl function, which has the signature foldl :: (a -> b -> a) -> a -> [b] -> a.
The (a -> b -> a) section of foldl is a function which takes two values and returns one of the first type. In our case, this should be our comparison function:
myMax :: Ord a => a -> a -> a
myMax x y | x > y = x
| otherwise = y
(note that Ord a is required so that we can compare our values).
So, we can say
-- This doesn't work!
myMaximum :: Ord a => [a] -> a
myMaximum list = foldl myMax _ list
But what is _? It doesn't make sense to have a starting value for this function, so we turn instead to foldl1, which does not require a starting value (instead it takes the first two values from the list). That makes our maximum function
myMaximum :: Ord a => [a] -> a
myMaximum list = foldl1 myMax list
or, in pointfree format,
myMaximum :: Ord a => [a] -> a
myMaximum = foldl1 myMax
If you look at the actual definition of maximum in Data.List, you'll see it uses this same method.
map maps a function over a list. It transforms each thing1 in a list to a thing2.
What you want is to find the biggest difference between two neighbours, which you can't do with map alone. I'll assume you're only looking at numbers for now, because that's just easier.
diffs :: (Num a) => [a] -> [a]
diffs [] = []
diffs [x] = []
diffs (x1:x2:xs) = abs(x1-x2) : (diffs$x2:xs)
mnd :: (Num a, Ord a) => [a] -> a
mnd [] = 0
mnd [x] = 0
mnd xs = maximum$diffs xs
So diffs takes each list item one at a time and gets the absolute difference between it and its neighbour, then puts that at the front of a list it creates at it goes along (the : operator puts an individual element at the front of a list).
mnd is just a wrapper around maximum$diffs xs that stop exceptions being thrown.

Composing a chain of 2-argument functions

So I have a list of a functions of two arguments of the type [a -> a -> a]
I want to write a function which will take the list and compose them into a chain of functions which takes length+1 arguments composed on the left. For example if I have [f,g,h] all of types [a -> a -> a] I need to write a function which gives:
chain [f,g,h] = \a b c d -> f ( g ( h a b ) c ) d
Also if it helps, the functions are commutative in their arguments ( i.e. f x y = f y x for all x y ).
I can do this inside of a list comprehension given that I know the the number of functions in question, it would be almost exactly like the definition. It's the stretch from a fixed number of functions to a dynamic number that has me stumped.
This is what I have so far:
f xs = f' xs
where
f' [] = id
f' (x:xs) = \z -> x (f' xs) z
I think the logic is along the right path, it just doesn't type-check.
Thanks in advance!
The comment from n.m. is correct--this can't be done in any conventional way, because the result's type depends on the length of the input list. You need a much fancier type system to make that work. You could compromise in Haskell by using a list that encodes its length in the type, but that's painful and awkward.
Instead, since your arguments are all of the same type, you'd be much better served by creating a function that takes a list of values instead of multiple arguments. So the type you want is something like this: chain :: [a -> a -> a] -> [a] -> a
There are several ways to write such a function. Conceptually you want to start from the front of the argument list and the end of the function list, then apply the first function to the first argument to get something of type a -> a. From there, apply that function to the next argument, then apply the next function to the result, removing one element from each list and giving you a new function of type a -> a.
You'll need to handle the case where the list lengths don't match up correctly, as well. There's no way around that, other than the aforementioned type-encoded-lengths and the hassle associate with such.
I wonder, whether your "have a list of a functions" requirement is a real requirement or a workaround? I was faced with the same problem, but in my case set of functions was small and known at compile time. To be more precise, my task was to zip 4 lists with xor. And all I wanted is a compact notation to compose 3 binary functions. What I used is a small helper:
-- Binary Function Chain
bfc :: (c -> d) -> (a -> b -> c) -> a -> b -> d
bfc f g = \a b -> f (g a b)
For example:
ghci> ((+) `bfc` (*)) 5 3 2 -- (5 * 3) + 2
17
ghci> ((+) `bfc` (*) `bfc` (-)) 5 3 2 1 -- ((5 - 3) * 2) + 1
5
ghci> zipWith3 ((+) `bfc` (+)) [1,2] [3,4] [5,6]
[9,12]
ghci> getZipList $ (xor `bfc` xor `bfc` xor) <$> ZipList [1,2] <*> ZipList [3,4] <*> ZipList [5,6] <*> ZipList [7,8]
[0,8]
That doesn't answers the original question as it is, but hope still can be helpful since it covers pretty much what question subject line is about.

Resources