"a"
'a'
''
in Python and Haskell. In Haskell, the first one is a list of Char (which happens to be exactly the same thing as a String) the second one is a single Char, and the third one doesn't exist because you can't have an empty Char like you can an empty String, because while a String is a list and has a length, which can be zero, a Char is a character, not a list, and simply can't be "empty" in this way. In Python, all these things are strings, and single and double quotes are just alternative syntax for strings.In C you also have a char type and you also have single-quoted char literals, and the third line is a compile-time error in C too for exactly the same reason, because a char literal represents a char, not a list of anything, and so it can't be empty in the way that a string can be empty.
From the post:
> Python doesn't distinguish between Char and String types with single and double quotes, so empty char is empty string is an empty list.
This is basically right, but the subtle misunderstanding here is that there is no such thing an empty char or indeed a char of any kind in Python, the closest you have is chr() and ord() to get integers out of strings. Indexing a string just gives you another string of length one, there's no char type in Python. If you wanted to implement cons, car, and cdr in Python such that (cons (car "abc") (cdr "abc")) = "abc" you wouldn't be able to do it in the natural way, because the type that (car "abc") needs to have doesn't exist in the language. Strings in Python are almost a list type but the elements of that list aren't representable.
So, coming from Python, the misunderstanding about why there's no "base case" for char as there is for string is understandable, but it doesn't really have much to do with Haskell or with any kind of runtime error handling.
Sure you can!
>>> car = lambda x: x[0]
>>> cdr = lambda x: x[1:]
>>> cons = lambda x, y: x + y
>>> cons (car("abc"), cdr("abc"))
'abc'For a correct implementation of cons, the following gives a list '("a" "b") of two strings:
(cons "a" (cons "b" ()))
and (list "a" "b") is just sugar for the above. This doesn't work if you redefine cons on strings to mean concat.In other words, if (cons "a" "bc") gives you "abc", then it's not cons.
>Once you understand that, this confusion makes about as much sense as expecting an integer to be "empty".
My comment was in reference to the above. An empty number does make sense because of the of 1/0. There is nothing wrong in including a null in a set if there are operations that are illegal. Such operations can't return something so you can use a null to represent the absence of something. This is entirely different from an empty container type.
>So, coming from Python, the misunderstanding about why there's no "base case" for char as there is for string is understandable
The author is looking for a null value not an empty container type or "base case". I quote: "Haskell has no null definition of type Char,"
Basically he's looking for the equivalent of "None" in haskell. He has slight confusion in thinking that an empty container type or empty string is equivalent to a "None."
Because he literally said he's looking for a null he is indeed talking about all things associated with null.
The closest thing to null in Haskell is the maybe monad which is like null but without a runtime exception.
His confusion arises because he feels that if in haskell you can do this:
stringfunction (x:xs) = ...
stringfunction [] = ...
Then you should be able to do this: charfunction x = ...
charfunction null = ...
which is impossible in haskell, so--->I'm saying that similar functionality can be achieved with:
charfunction (Just x) = ...
charfunction Nothing = ...
The above is 100% handling a null, one of the main sources of runtime errors.I'm pointing out the different semantics of single-quoted literals as the source of the confusion, with "empty char" being directly mentioned in the post as something that exists in Python, which, as of course you know, it doesn't.
If the confusion was about how to add a nothing value to a type, why did it arise specifically in regards to Char, and not, say, Integer? The question "why are the char types different between Haskell and Python in this way" is best answered by pointing out that Python doesn't have a char type at all, and all of these things are strings.
Of course, you're entirely correct that you can use a sum type to handle nulls.
I illustrated earlier because of the similarity between char and str, he believed that because of recursive functions on str has base case [], he believed that char needed one too.
*** Exception: Prelude.head: empty list• In Java forgetting to check for null will crash your program.
• In C, forgetting to check for NULL will lead to undefined behavior.
• In Haskell, you either have to (a) check for Nothing, or (b) explicitly say "crash the program if this is Nothing" by using a function like fromJust or head, which internally does (a). This is always an improvement over C, and often an improvement over Java.
Side note: I like Rust's convention of consistently naming these might-crash-your-program methods "unwrap" and "expect".
I get the choice of why it wasn't done with haskell even though I disagree with it.
Yes, I'm aware of that 1/0 returns an exception.
I'm saying that it makes sense to use nulls because 1/0 is an undefined operation. Therefore to represent the output of 1/0 with a null makes sense. So the existence of nulls makes sense. That is not haskell code I'm typing up there. "1/0 = undefined" is not code.
If you read my reply you will see that my statement isn't referring to an exception caused by 1/0. It's actually referring to a null exception which is completely different.
I am explicitly saying that the "null" in haskell is part of a sum type (the maybe monad) and in that case the "null" or nothing will be handled with no run time errors. You cannot have a null exception in haskell.
Never did I say Haskell has no runtime errors, and 1/0 is not an exception caused by a null. Please analyze and reread my statement.
Nothing to do with null per se.
Anyway, the real lesson is that even if your language has sane defaults that force you to check that your list isn't empty, or that your pointers aren't null, a bad library function like List.head can hide that check away from you and crash anyway, bringing you back to square one in terms of the type system preventing unexpected crashes.