Mark Karpov, a Haskell developer and author of Megaparsec, writes tutorials and opinion pieces on Haskell, OCaml and functional programming.
Commenters link his tutorials, like the one on GHC generics, as references for functional programming techniques and cite his Haskell versus OCaml comparison.
What means D < U in your notation? It's like, D is a subset of U?
Anyway the problem with trying to make a function total by fiat like with the definition (D, R, f) is that for some functions (even computable functions), its domain may be an uncomputable set.
That's because if we could decide if a given x in f(x) makes f return something or not, if f represents the output of a Turing machine we could use that to solve the halting problem.
So while in principle every function is total in some domain D (precisely the domain which it is defined to return some value), sometimes we can't talk very usefully about this domain. In this case, we choose to treat f as a partial function and talk about a larger set of values D' f receive as input (and might or might not return anything), for which D is a subset.
Some properties might not be literally undecidable but we may not know how to solve them yet. For example, the function collatz : Nat -> () [0] that receives a natural number and either return nothing if its collatz sequence[1] eventually reaches 1, or else run forever. What's the domain of this function? (you can switch this for any actually undecidable problem for extra hardness)
Unfortunately the linked article doesn't talk about this particular motivation for defining partial functions in computer science, and instead cites sqrt as an example, even though it has a perfectly computable domain. The domain of sqrt is just the non-negative reals; which we could give a more…
I like the article, but I don't agree with the conclusion.
> When profiling, it is important to always run your code over an extended period of time, preferably seconds, in order to smooth out any irregularities that may occur.
I agree. Moreover, for very small differences (1%-5%) I prefer that the benchmark is long enough that the difference is at least 200ms, or preferably 1 second.
> When running extremely short pieces of code, in this case, around 500 nanoseconds (The original measurements did not have units, this is an educated guess).
I looked at the other article https://markkarpov.com/post/migrating-text-metrics.html . The graph of the times don't look like the graph of a benchmark of 500ns~=1us. They are too clean, a graph of a measurement of a few ms is very noisy, lots of crazy values that make the results impossible to compare. The graphics have two nice sets of bars that are apparently following a smooth curve (not drawn in the graph). So it was definitively not a measurement of a few .5us. He was repeating it many times to get a long enough time.
> In my benchmarks, I’ve made the C code and the Haskell code run on inputs of 100 thousand, 1 million, and 10 million long strings to get some reliable measurements. Each measurement is done 1000 times to make sure we can obtain some decent results.
For comparison, the original article uses strings of length 8--160, i.e. 1000 times smaller. So he is now changing the benchmark. Perhaps Haskell has a sweet spot…
You are not capable of writing a PhD chapter. You lack the basic scientific thinking or method required. I suggest you focus on your core competency - chuckling.
Here is some basic reading material for you. Lets see if you can follow this
https://hackage.haskell.org/package/async-2.2.2/docs/Control-Concurrent-Async.html
do a1 <- async (getURL url1)
a2 <- async (getURL url2)
page1 <- wait a1
page2 <- wait a2
This implements async await in Haskell via a library using Monads. Hint: you cannot do this in C via any library. You need to modify the language. If you are uneducated about Monads, you might think this is similar to some kind of C code. It is not!
Here is the same do syntax being used to parse source files instead of doing async/await IO. Yes this parser will also perform backtracking etc, which will not be obvious to anyone who thinks this is some kind of transliteraton of C
https://markkarpov.com/tutorial/megaparsec.html
mySequence :: Parser (Char, Char, Char)
mySequence = do
a <- char 'a'
b <- char 'b'
c <- char 'c'
return (a, b, c)
Hint: If you want to do something like this in C, you use yacc/bison, a parser generator tool - there is no library. And it is fucking ugly.
Hint 2: There are monads for parsing, async IO, exceptions, nondetermenism, monte carlo .........
Hint 3: The parser libraries are 25 years old.
Hint 4: Monad libraries represent less than 1% of PL research over the last 40 years.
Hint 5: C# LINQ is…
Not the OC and (mostly) not related to Haskell performance, but the following could be useful to you:
State of Haskell Ecosystem: https://github.com/Gabriel439/post-rfc/blob/master/sotu.md#compilers
Guide to Haskell (2018): https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/
Haskell learner milestones: https://stackoverflow.com/a/1016986
GHC optimizations (rather advanced, but interesting): https://stackoverflow.com/questions/12653787/what-optimizations-can-ghc-be-expected-to-perform-reliably
Compilation of various Haskell tutorials on more advanced topics: https://markkarpov.com/learn-haskell.html
Despite the popular saying, there’s a wealth of knowledge about Haskell beyond just academic papers, and in digestable form, but it’s scattered around little-known blogs. I’d recommend you to check out r/haskell from time to time, or subscribe to the Haskell newsletter - at least for me, those are the places where I got to know most of Haskell blogs.
I don't like Exceptions in OCaml (or Haskell or C++ or JS/TS) either. But they aren't unsafe, except for bugs in the compiler/runtime (in OCaml, in C++ there are of course some footguns ;).
Of course they add "bottom" to any function where an Exceptions can occur (and other things, see for example
https://markkarpov.com/tutorial/Exceptions) but for stuff like `0/0` there are 3 possibilities:
use something like `Maybe` for the result - clumsy.
return (for example) 0, which is what most theorem provers (like Coq or Lean and dependently typed languages like Idris) do, that need their functions to be total
or throw an exception.
Now, with OCaml's effect systém, there also is no need to use exceptions for control flow - which you should have never done anyway.
Well, these are mutable, but only in the ST or IO monad - which is a guarantee, but at the same time it is not quite the same thing. You cannot just accumulate state by mutating some stuff, then in a separate part of the program mutate it again.
Which is intentional, of course, but quite opposite to the imperative mindset.
About {#- Language Strict #-}, thank you - I was not aware of it.
I think the first example in this post https://markkarpov.com/post/migrating-text-metrics.html makes it clear that, while one can program in imperative style, it is not at all natural, and it is going to introduce as much boilerplate as, say, trying to program in functional style in Java 6