Exceptions, especially asynchronous exceptions, can be tricky to reason about. That's why Edsko and I are going to talk about the bracket and generalBracket functions in today's 10th episode of the #Haskell Unfolder! Join us live on YouTube, 2023-08-30 at 1830 UTC (or watch the recording later).
Loving this book. Explanations are very clear, exercises make you think. #haskell#book Had the itch to dive into Haskell again, it’s been 13 years or so. Can’t hurt starting from the beginning again.
I should write a huge text about how #Haskell is a great language and is getting better all the time.
Just to dilute the stream of negativity constantly appearing on feeds.
Thank you @suppi for writing https://lhbg-book.link/ - a great practical introduction to #Haskell with everything you need to know to get started with real-world projects! Really enjoyed reading it and wish I had found this earlier. 👍
I often see in #Haskell codebases foo $ bar $ baz $ 5. Let's put it aside that I mostly agree with https://www.haskellforall.com/2015/09/how-to-make-your-haskell-code-more.html suggestion on foo (bar (baz 5)) and try to stick it whenever I can. But for the original, I always found foo . bar . baz $ 5 easier to read, and easier to refactor (just copy the dotty part to a helper). Don't be so $-oriented ;)
Brzozowski derivatives are neat, but good old denotational semantics of regular expressions can be very elegant too:
data RE = Empty | Eps | Ch Char | App RE RE | Alt RE RE | Star RE
foldRE :: p -> p -> (Char -> p) -> (p -> p -> p) -> (p -> p -> p) -> (p -> p) -> RE -> p
foldRE emp eps ch app alt star = go where
go = case
Empty -> emp
Eps -> eps
Ch c -> ch c
App p q -> app (go p) (go q)
Alt p q -> alt (go p) (go q)
Star p -> star (go p)
recognise :: RE -> String -> [String]
recognise =
foldRE (pure empty) pure (c -> case x : xs | c == x -> [xs]; _ -> [])
(>=>) (liftA2 (<|>)) (p -> fix (t -> liftA2 (<|>) pure (p >=> t)))
@mangoiv perhaps it is slightly easier to read like this?
data RE = Empty | Eps | Ch Char | App RE RE | Alt RE RE | Star RE
data REalg a = REalg
{ emp :: a
, eps :: a
, ch :: Char -> a
, app :: a -> a -> a
, alt :: a -> a -> a
, star :: a -> a
}
foldRE :: REalg a -> RE -> a
foldRE alg = go where
go = case
Empty -> emp alg
Eps -> eps alg
Ch c -> ch alg c
App p q -> app alg (go p) (go q)
Alt p q -> alt alg (go p) (go q)
Star p -> star alg (go p)
recognise :: RE -> StateT String [] ()
recognise = foldRE REalg
{ emp = empty
, eps = pure ()
, ch = c -> StateT (case x : xs | c == x -> [((), xs)]; _ -> [])
, app = (*>)
, alt = (<|>)
, star = p -> fix (t -> p *> t <|> pure ())
}
I really end up frustrated by both extremes of operator overloading in languages and language cultures.
Both the Java approach of "NEVER OVERLOAD THE OPERATORS" as well as the approach favored in #haskell and #ocaml of NO GODS NO MASTERS OPERATOR OVERLOADING FOR ALL.
@hrefna what I like about #haskell's "operator overloading" (type classes) is that it is customary to group operators together and give them laws which give them a coherent meaning across all instances. So you can't just abuse + for list concatenation.