• Declarative (thanks to Pattern-Matching with implicit binding and destructuring)
    • Even side-effecting IO operations are but a description of what to do (eventually).
    • Assumes no guarantee of certain order of evaluation. So-called Normal Order of evaluation. Like it is in a cell.
  • When performed.
    • The result of evaluation of the Main.main pure function is a protein-like structure to be executed by the runtime.
    • Technically - a type-checked description of a state-machine (a protein-like structure) to be ran by the runtime.

  • Equational Semantics (referential transparency)
    • Functions are defined as equations where the left hand side equals the right hand side.
    • Evaluation is done by rewriting of expressions - by substituting equal for equal after recursively applying predefined reduction rules. Like Maths.
    • Function defined by pattern-matching can be used of each sub-definition independently.
  • Statically Typed (at the cost of forced homogeneity of conditionals, product-types and recursive-types)
    • Every expression (which is reducible to a value) has a type which is determined at compile-time.
    • All the types composed together by function application have to be match up (to be consistent).
    • Compiler rejects the code which is not type-checked (prevents violations of contracts/discipline). Contradictions do not exist.
  • Pure Functional (ultimately, a program is a single referentially-transparnt expression returned by the main function)
    • Every function is a function in the mathematical sense (i.e., "pure").
    • Haskell runtime runs compiled (desugared) code of this expression, eventually performing all the side-effects and IO operations
    • Even side-effecting IO operations are but a descriptions of what to do, produced by pure code.
  • It is easy to fuse chains of functions together (making pipelines using function composition).
  • Lazy (so-called Normal Order of evaluation or call-by-name, on-demand)
    • Every subexpression in implicitly wrapped into a thunk, which is type-tagged in the static environment.
    • Functions don't evaluate their arguments - they are passed as expressions (thunks).
    • Gives the ability to write control constructs (such as if/else) just by writing normal functions. (a-la macros which do not evaluate - only rewrite!)
    • Using expression in definition of itseves - (==) (the one I’m defining). When eventually it would be used it will be already defined!
      instance Eq Point where
          (Pt x y) == (Pt x' y')   =   x == x' && y == y'
  • Homogeneous conditionals: if-then-else (a syntactic sugar for a case expression)
    • In a lazy language conditional could be viewed as a function has type Bool->a->a->a.
    • Short Circuiting is in every expression, due to lazyness.
  • Homogeneous lists, tables (maps).
  • Tuples and Records (sum-types) for heterogeneous data.
  • Immutability allows decomposition back to the parts (atoms) and reuse of the parts without any concerns such as locking (referential transparency)
    • Pattern-matching decomposes and rebind the parts of structures (destructuring). Each part could be safely passed (referenced) everywhere.
  • Parametric polymorphism - generalize (quantify)
    • An implicit parameter (a context) which is a value of an actual type.
  • Type-classes (ad-hoc polymorphism) - a named set of interfaces
    • Generalize - define a set of interfaces - define what it takes to be a _____
    • Specialize - provide implementations - what it takes to be a _____ (implement an interface constrained by the type-class)
    • The type a is of the class Eq if it implements the function (==) that takes two arguments of type a and returns a Bool.


Haskell's I/O system is built around a somewhat daunting mathematical foundation: the monad. However, understanding of the underlying monad theory is not necessary to program using the I/O system. Rather, monads are a conceptual structure into which I/O happens to fit.

Monad is an abstraction for general notion of transition from one state to another. Semantically they are explicit transitions from one state to another in an state-machine which is what every program is.

See also Kundalini

Side Effects

So called "actions", usually instances of a Monad, are descriptions of side-effects which will be performed eventually by the runtime.

"Actions" don't do anything until they are sequenced appropriately using a do-notation expression (a syntactic sugar), which implicitly serializes actions using nesting of function calls (this is how a function composition is actually implemented) - the only way to ensure the evaluation order in a lazy language - by producing a single expression of nested function application (like \x -> \y -> x + y, which, unsurprisingly, looks like currying) to be eventually evaluated.

The Haskell runtime runs the main function to get this description of all your effects, and then runs the effects per your instructions.

Unnecessary wrapping

Haskell in the hands of narcissistic idiots is famous for unnecessary wrapping of everything into everything - unnecessary type-classes, more and more general monads, etc. Tooling is not an exception.

$ stack ghci --with-ghc intero
Last modified 8 months ago Last modified on Nov 13, 2018, 9:05:18 AM