Skip to content

Instantly share code, notes, and snippets.

@hanshoglund
hanshoglund / adts3.py
Last active June 25, 2024 12:23
adts3.py
from typing import Generic, TypeVar, final
from typing import Callable
from dataclasses import dataclass
L = TypeVar("L")
R = TypeVar("R")
class Either(Generic[L,R]): ...
@final
@hanshoglund
hanshoglund / adts2.py
Last active June 25, 2024 12:24
adts2.py
# mypy 1.8.0, python 3.12
# supports recursion + generics unlike (https://gist.github.com/hanshoglund/cd4302f2c1e07370649b5983bead320e)
# OTOH, no exhaustive checks in match clasuses. And issues like: https://gist.github.com/hanshoglund/d255b3a9ff0a07cf7504a3b4313d6d7c
#
# Note: if we ONLY use frozen dataclasses as here, we have Eq,Ord,Show,Hashable, so the need for ad-hoc polymorphism is greatly reduced
from typing import Generic, TypeVar, final
from typing import Callable
from dataclasses import dataclass
T = TypeVar("T")
@hanshoglund
hanshoglund / adts.py
Last active June 25, 2024 12:24
Python ADTs
# tested with mypy 1.7.1, python 3.12
# Recursive ADT with exhaustive pattern matching
# Adapted from http://blog.ezyang.com/2020/10/idiomatic-algebraic-data-types-in-python-with-dataclasses-and-union/
#
# No generic/param polymorphic trees :(
# In many cases generic builtins (list, dict) + non-generic recursive ADTs (dataclasses) is enough
# How about optional/either. In go NIL us used, in mypy there is optional and either can be encoded (though there are bugs):
# https://github.com/python/mypy/issues/6748
#
@hanshoglund
hanshoglund / refl.ml
Last active September 20, 2023 00:09
refl.ml
(* supertype *)
type (_, _) st =
| Refl : ('a, 'a) st
| Fst : ('a, 'a * 'b) st
| Snd : ('a, 'b) st -> ('a, 'c * 'b) st
| Left : (('a, 'b) result, 'a) st
type _ ttype =
| Int : int ttype
| Bool : bool ttype

(Quick notes written up in response to a discussion on defaulting in GHC.)

Though I argued that implicitly imported defaults should be opt-in, I have a use case for them in this library. It makes heavy use of "custom literals", defined thus:

class IsPitch a where
  c :: a
  d :: a
  -- etc
@hanshoglund
hanshoglund / Golden Tests.md
Last active September 15, 2020 21:56
Golden Tests.md

In the following, we'll assume a setting with a Git repo, master branch and pull requests (e.g. Github). We'll also assume that some tests are run on each pull request to verify correctness of a commit which is a candidate to be the next state of the master branch, of which is a direct descendant (whether this candidate is authored by a user or the result of an auto-merge between a user-authored commit and the last master branch doesn't matter).

What is a "normal test"?

A predicate run on the candidate commit x. P :: Commit -> Bool.

What is a "golden test"?

In principle a function Commit -> A (e.g. running a compiler on some source code) and some predicate A -> A -> Bool (e.g. ==).

@hanshoglund
hanshoglund / DhallSchema.md
Last active September 8, 2020 21:20
Dhall schema extensions

Useful Dhall operations/tools:

Common subexpr elimination:

[ < L | R | X >.L, < L | R | X >.R ] 
  =cse=> 
let _ = < L | R | X > in [ _.L, _.R ]

For any expr e

The Monoid instance for Data.Map comes with a left-biased Monoid instance:

> Map.fromList [(1,'a')] <> Map.fromList [(1,'b'),(2,'x')]
Map.fromList [(1,'a'),(2,'x')]

While this is a perfectly valid monoid it might not alwasy be the one that you want. Thanks to -XDerivingVia we can now choose our own instance. This newtype behaves like a traditional map, except that it merges colliding elements pointwise.

RIO and impure monads

This post was prompted by a colleague asking for my opinion about the RIO type. In brief, I like it and recommend it, but for impure monads only.

To recap, RIO is defined like this:

data RIO env a = RIO { runRIO :: env -> IO a }

Finally Free

{-# LANGUAGE RankNTypes, DeriveFunctor, TypeOperators, KindSignatures, GADTs, DataKinds, MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
import Control.Applicative
import Data.Semigroup(Semigroup(..))

If you've programmed in Haskell you may have come across free construcions: free monoids, free monads, free applicatives and so on. But what does it actually mean for an object to be "free"?