Skip to content

Instantly share code, notes, and snippets.

@evancz
evancz / Architecture.md
Last active December 21, 2022 14:28
Ideas and guidelines for architecting larger applications in Elm to be modular and extensible

Architecture in Elm

This document is a collection of concepts and strategies to make large Elm projects modular and extensible.

We will start by thinking about the structure of signals in our program. Broadly speaking, your application state should live in one big foldp. You will probably merge a bunch of input signals into a single stream of updates. This sounds a bit crazy at first, but it is in the same ballpark as Om or Facebook's Flux. There are a couple major benefits to having a centralized home for your application state:

  1. There is a single source of truth. Traditional approaches force you to write a decent amount of custom and error prone code to synchronize state between many different stateful components. (The state of this widget needs to be synced with the application state, which needs to be synced with some other widget, etc.) By placing all of your state in one location, you eliminate an entire class of bugs in which two components get into inconsistent states. We also think yo
@evancz
evancz / Focus.md
Last active March 3, 2017 14:58
Potential outline of a simple and useful lens library for Elm

Focus

A Focus is a way to work with particular parts of a large chunk of data. On the most basic level, it lets you get and set fields of a record in a simple and composable way. This means you could avoid writing special record update syntax and use something that composes much more elegantly.

This API is inspired by the concept of Bidirectional Lenses as described by Nate Foster and seen in a modified form in Haskell as "lenses" and in ClojureScript as "cursors". My personal opinions and understanding comes from this talk by Simon Peyton Jones, discussions with @seliopou, and a basic understanding of Nate Foster's PhD thesis on bidirectional lenses. I chose the name "Focus" for this outline because it is sort of like a lens that only lets you see in one direction.

Here's the pseudocode that describes the basic API:

modul
@evancz
evancz / ThwompBlinks.elm
Created May 20, 2014 22:35
Thwomp program, except Thwomp blinks periodically
@evancz
evancz / Triangle.elm
Created May 15, 2014 02:08
A triangle that rotates, but done in a slow way. Ideally you send a transformation matrix to the GPU rather than a whole new entity!
import Math.Vector3 (..)
import Math.Matrix4 (..)
import Graphics.WebGL (..)
-- This is the main function
-- It renders the triangle then prints the points
-- of the triangle
-- Notice how the points change but the render doesn't
main = flow down <~ ( combine [ render <~ updateTriangle
@evancz
evancz / History.elm
Last active November 23, 2015 20:44
module History where
type History state value =
{ state | undo : History s a -> Maybe (History s a)
, redo : History s a -> Maybe (History s a)
, add : a -> History s a -> History s a
, value : History s a -> Maybe a
}
type Infinite a = { past : [a], future : [a] }
@evancz
evancz / History.elm
Created April 23, 2014 19:39
General purpose library for undo/redo. Inspired by David Nolen's talk at Philly ETE.
module History where
data Step a = Undo | Redo | Checkpoint a | NoCheckpoint a
foldp : (a -> b -> b) -> b -> Signal (Step a) -> Signal b
-- It could also be interesting to have custom strategies for keeping track
-- of history. Maybe you want to have a fixed size stack, infinite stack,
-- stack with exponential decay, etc.
type Point3D = { x : Float, y : Float, z : Maybe Float }
deriving Json, New, Binary
-- Resulting in the folliwing derived values:
Point3D.Json.encode : Point3D -> String
Point3D.Json.decode : String -> Maybe Point3D
Point3D.New : Float -> Float -> Maybe Float -> Point3D
@evancz
evancz / Checks.elm
Created February 27, 2014 16:11
A bunch of synced check boxes
import Graphics.Input as Input
main : Signal Element
main = lift display check.signal
check : Input.Input Bool
check = Input.input True
display : Bool -> Element
display checked =
import Char (isDigit)
import String (all)
import Graphics.Input (Input, input, FieldContent, noContent, field)
main : Signal Element
main = scene <~ content.signal
content : Input FieldContent
content = input noContent
module Animal where
data Either a b = Left a | Right b
-- Left 42 <=> { "tag":"Left", "contents":[42] }
-- Right "cat" <=> { "tag":"Right", "contents":["cat"] }
port animal : Signal (Either Int String)
port animal = always (Right "cat") <~ every second