Developers roadmap
Inspired by developers-roadmap.
Extensions:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE InstanceSigs #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}
Imports
import Control.Monad.Fix (fix)
import Language.Haskell.TH.Syntax (Dec, Quasi, runQ)
main = undefined
Kinds
-
DataKinds
- src- What is the data type promotion?
- promote terms to type level like
'[1,2,3]
- promote terms to type level like
- What is the data type promotion?
-
Are types with promoted kinds inhabited?
- inhabited types (types that have at least 1 value) are of kind Type
-
ConstraintKinds
-Constraint
s as first-class citizenstype Stringy a = (Read a, Show a)
-
Symbol
- a compile-time string- UnconsSymbol
- typed-interpolation - a good parsing example.
Functional dependencies
-
Set a relation between types. Make one type correspond to another type
class (Monad m) => MonadError e m | m -> e where throwError :: e -> m a catchError :: m a -> (e -> m a) -> m a
-
Problem (src):
we want a MonadReader typeclass where there is only a single instance per m, and we know the env parameter that will be available from each m.
- Approach 1:
-
MultiParamTypeClasses
let us specify explicitly what theenv
is -
FunctionalDependencies
allow us to constrain ourselves to a single instance.newtype PersonReader a = PersonReader { runPersonReader :: Person -> a } deriving Functor class MonadReader env m | m -> env where ask :: m env instance MonadReader Person PersonReader where ask = PersonReader $ \env -> env instance MonadReader env (Reader env) where ask = Reader $ \env -> env greeting :: PersonReader String greeting = do person <- ask -- Here, derives that `person :: Person` -- from `instance MonadReader Person PersonReader` -- via fundep `m -> env` and `ask :: m env` pure $ show person
-
Approach 2:
-
TypeFamilies
class MonadReader m where -- use an associated type type Env m ask :: m (Env m) data Person newtype PersonReader a = PersonReader (a -> a) -- `m (Env m)` calculates to `PersonReader Person` instance MonadReader PersonReader where type Env PersonReader = Person ask :: PersonReader (Env PersonReader) ask = PersonReader id
-
-
- Approach 1:
Laziness
-
Bang patterns
{-# LANGUAGE BangPatterns #-}
addBang :: Int -> Int -> Int
addBang !x !y = x + y
-- equivalent to
addSeq :: Int -> Int -> Int
addSeq x y = x `seq` y `seq` x + y
-
$!
- strict application -
Thunk
is an unevaluated expression - srcfree variables
in an unevaluated expr- when evaluated, the pointers to it will point to the result
- a
dead thunk
isgarbage collected
-
Expression forms - src
-
Normal form
An expression in normal form is fully evaluated, and no sub-expression could be evaluated any further (i.e. it contains no un-evaluated thunks).
-
Weak head normal form
An expression in weak head normal form has been evaluated to the outermost data constructor or lambda abstraction (the head).
(1 + 1, 2 + 2) -- the outermost part is the data constructor (,) \x -> 2 + 2
-
-
a `seq` b
- evala
toWHNF
, returnb
-
a `deepseq` b
- evala
toNF
, returnb
-
force b = b `deepseq` b
- evalb
toNF
and returnb
- If we have
let a = force b
,a
is not inNF
- To get
a
inNF
, we need to!a
- If we have
-
Thunks, Sharing, Laziness via
ghc-viz
(available innixpkgs
) -
- force impure exceptions using
tryAnyDeep
andNFData
.
- force impure exceptions using
Fix combinator
ex13 :: [Int] -> Int
ex13 =
fix
( \t c ->
\case
(a0 : a1 : as) -> t (c + fromEnum (signum a0 /= signum a1)) (a1 : as)
_ -> c
)
0
-- >>>ex13 [-3,0,2,0,5]
-- 4
File IO
- There are several representations of text in
Haskell
-ByteString
,Text
,String
ByteString
can contain bothhuman-readable
orbinary
data that mustn't be mixed- Also, there are many
file encodings
. UseUTF-8
to be safe - One can encode standard data types into a
ByteString
using Data.ByteString.Builder LBS
reads files in chunks. Can be used for streaminghGet
reads a given number of bytes from a handlestdout
andstdin
are files- Can set buffering mode on a handle:
hSetBuffering stdout NoBuffering
Debugging
Debug.Trace
breakpoint
- src- put breakpoints into an app (see TryBreakpoint)
- inspect variables visible at a breakpoint
- freeze other threads (
GHC 9.2.x+
)
Monoid
- List comprehension
- Skip elements
-
On a flag
_deepClone :: Bool _deepClone = True s1 :: [String] s1 = ["--deepClone" | _deepClone]
-
On a pattern fail
catMaybes :: [Maybe a] -> [a] catMaybes ls = [x | Just x <- ls]
-
- Skip elements
Template Haskell
-
print the structure of an expression
ex2 :: (Quasi a) => a [Dec] ex2 = runQ [d|decl :: Int; decl = 1 + 2|] -- >>>ex2 -- [SigD decl_0 (ConT GHC.Types.Int),ValD (VarP decl_0) (NormalB (InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) (Just (LitE (IntegerL 2))))) []]
Higher-Kinded Data
- Defaulting fields in a record (via HKD) - GH
- Higher-Kinded Data
Generics
- Higher-Kinded Data
aeson
converts data to generic representation.- Its functions for parsing use selector names, modify them via options, then convert to or parse JSON.
QualifiedDo
- Qualified do: rebind your do-notation the right way
-
example
{-# LANGUAGE QualifiedDo #-} module Main where import Data.Function((&)) (>>=) = (&) foo :: Int foo = Main.do z <- (3, 4) (x, s) <- z x main = print foo
-
Effects
Effectful
String interpolation
Optics
Monad transformer stack
- Determine the type - SO
UnliftIO
- Demystifying MonadBaseControl
- Capture the action’s input state and close over it.
- Package up the action’s output state with its result and run it.
- Restore the action’s output state into the enclosing transformer.
- Return the action’s result.
Handle pattern
-
Take functions from a given environment, e.g. from
ReaderT
Data
- large-records
- Avoid quadratic Core size - advice
GHCJS
- rzk-lang/rzk - see
flake.nix
Nix
- To keep completions in share, need to modify justStaticExecutables
so that it doesn't remove
share
.
Misc
- Радости и горести побед над C: делаем конфетку из прототипа wc на хаскеле
- Parsing with Haskell
- Haskell CI with caching - src
string-interpolate
- src- UTF-8 string interpolation
- ViewPatterns