Skip to content

Instantly share code, notes, and snippets.

@Savelenko
Last active January 26, 2024 08:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Savelenko/d97a7897ee2f7c8a04fae37be4eb3848 to your computer and use it in GitHub Desktop.
Save Savelenko/d97a7897ee2f7c8a04fae37be4eb3848 to your computer and use it in GitHub Desktop.
Family tree zipper example
module Zipper
type Person = { Name : string; Children : List<Person> }
type Parent = Parent of {| Name : string; OtherChildren : List<Person> |} // Note *other* children
/// The "family tree" zipper
type FamilyTree = FamilyTree of Person * List<Parent> // A person and his/her ancestors
/// Person -> FamilyTree
///
/// "Start" a family tree with the given person as the family head. See also `familyHead`.
let familyTree person = FamilyTree (person, [])
/// FamilyTree -> Person
///
/// Whom the given family tree is currently focused at.
let who (FamilyTree (person, _)) = person
/// Name -> FamilyTree -> FamilyTree
///
/// In a family tree, go "down" to the child of `who` with the given name. No effect if `who` does not have a
/// child with that name.
let toChild childName (FamilyTree (person, ancestors)) =
match person.Children |> List.partition (fun c -> c.Name = childName) with
| [child], siblings -> FamilyTree (child, Parent {| Name = person.Name; OtherChildren = siblings |} :: ancestors)
| _ -> FamilyTree (person, ancestors)
/// FamilyTree -> FamilyTree
///
/// In a family tree, go "up" to the parent of `who`. No effect if `who` is the "family head", i.e. a person without the
/// recorded parent.
let toParent (FamilyTree (person, ancestors)) =
match ancestors with
| [] -> FamilyTree (person, ancestors)
| Parent parent :: olderAncestors ->
let parent = {
Name = parent.Name
Children = person :: parent.OtherChildren
}
FamilyTree (parent, olderAncestors)
/// FamilyTree -> Person
///
/// The family head of the given family tree. See also `familyTree`.
let rec familyHead (FamilyTree (person, ancestors) as familyTree) =
match ancestors with
| [] -> person
| _ -> familyHead (toParent familyTree)
/// Name -> FamilyTree -> FamilyTree
///
/// Record a new child of `who` in the given family tree.
let addChild childName (FamilyTree (person, ancestors)) =
let newChild = {
Name = childName
Children = []
}
FamilyTree ({ person with Children = newChild :: person.Children }, ancestors)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment