Below, I have collected several examples of "uncanny" Haskell. These are things that may be surprising to those who have learned the language merely by doing.
IO Type
The
IO
type is in fact not primitive to Haskell and is defined as follows:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
While this is not exactly uncanny (it is deep and well-engineered), it is
likely unfamiliar to many; we can in fact pattern match our way out of IO
.
Empty where
Clauses
The following is valid:
x :: Int
x = 3
where
Empty let
Declarations
The following is valid:
x :: Int
x = let in 3
Empty data
Declarations
The following is from
ghc-prim
:
data RealWorld
This has real-world use, much like the definition for IO
.
Functorial Template Haskell
As template Haskell at the top level is a Haskell value of type Q [Dec]
, we can do the
following:
fold <$> traverse makeLenses [''Type, ''SomeOtherType, ''YetAnotherType]
Otherwise
otherwise
is defined in Data.Bool
as
otherwise :: Bool
otherwise = True
Thus it makes for clearer guards, but it could in principle be overloaded or misused, viz.
subsets = filterM (pure [otherwise, False])
Function-Level Recursion
Function-level recursion is possible (and even desirable!) in Haskell. The classic example is recursion schemes; below is a hylomorphism.
hylo :: Functor f => (f b -> b) -> (a -> f a) -> a -> b
hylo f g = h where
h = f . fmap h . g
Void Semigroup
The Void
type is much like the RealWorld
type; it has no constructors.
data Void
We can define a Semigroup
instance for it, viz.
instance Semigroup Void where
a <> _ = a
We can also use another bit of uncanny Haskell to define absurd
: an empty
case
statement.
absurd :: Void -> a
absurd a = case a of {}
Main
Haskell accepts any value in the IO
monad as main
, and in fact you can write
main :: IO Int
main = pure 4
Rearranged Definitions
Haskell allows type signatures to be placed anywhere in the file. Thus one could write:
plusOne :: Int -> Int
timesTwo :: Int -> Int
timesTwo = (*2)
plusOne = (+1)