Haskell compiler mask: why do you need special handling of the compiler?

When trying to learn Haskell, one of the difficulties that arises is the ability when something requires special magic from the compiler. An example is the function seq , which cannot be defined, i.e. You cannot force the seq2 function to behave exactly like the built-in seq . Therefore, when teaching someone about seq you should mention that seq is special because it is a special character for the compiler.

Another example would be do -notation, which only works with instances of the Monad class.

Sometimes this is not always obvious. For example, sequels. Does the compiler know about Control.Monad.Cont or is it a simple old Haskell that you could come up with yourself? In this case, I think nothing special is required from the compiler, even if the sequels are a very strange kind of beast.

The language extensions are set aside, what do other magicians of the Haskell compiler need to know?

+7
haskell ghc
source share
2 answers

Almost all ghc primitives that cannot be implemented in userland are in the ghc-prim package. (he even has a module named GHC.Magic !)

Thus, browsing will make good sense.

Please note that you should not use this module in user code unless you know exactly what you are doing. Most of the useful material from it is exported to modules downstream in the base , sometimes in a modified form. These downstream locations and APIs are considered more stable, and ghc-prim makes no guarantees as to how it will work from version to version.

GHC-specific specifications are re-exported to GHC.Exts , but there are many other things in the Prelude (such as basic data types as well as seq ) or concurrency libraries, etc.

+5
source share

Polymorphic seq definitely magical. You can implement seq for any particular type, but only the compiler can implement one function for all possible types [and avoid optimizing it, even if it looks non-op].

Obviously, the whole IO monad is deeply magical, like everything with concurrency and parallelism ( par , forkIO , MVar ), volatile storage, throwing and catching for exception, garbage collector and runtime statistics, etc.

IO monad can be considered as a special case of ST monad, which is also magic. (It allows truly mutable storage that requires low-level material.)

State monad, on the other hand, is completely custom user level code that anyone can write. So the monad is Cont . Various monads of exceptions / errors are also possible.

Everything related to the syntax (do-blocks, list comprehensions) is rigidly tied to the definition of the language. (Note that some of them respond to the LANGUAGE RebindableSyntax , which allows you to change which functions it binds.) Also deriving stuff; the compiler “knows about” several special classes and methods for automatically generating instances for them. However, for newtype works for any class. (It simply copies an instance from one type to another identical instance of that type.)

Arrays are hard-wired. Like any other programming language.

The entire external interface of the function is clearly hard-wired.

STM can be implemented in user code (I did this), but it is currently tightly coupled. (I believe this provides a significant performance advantage. I have not actually tried to measure it.) But, conceptually, this is just an optimization; you can implement it using existing lower level concurrency primitives.

+1
source share

All Articles