Skip to content

Instantly share code, notes, and snippets.

@gilbert
Last active August 29, 2015 14:06
Show Gist options
  • Save gilbert/7d23274e9cd979292ebe to your computer and use it in GitHub Desktop.
Save gilbert/7d23274e9cd979292ebe to your computer and use it in GitHub Desktop.
Arrow Programming Language Sketch
module Library do
record Author do
fields do
name :: String
end
name :: Book -> String
name { author: authorName } = authorName
end
record Genre do
fields do
name :: String
end
end
record Book do
fields do
title :: String
genre :: Genre
authors = [] :: [Author]
end
hasAuthor :: Book -> Author -> Bool
hasAuthor book author = any (\a a.name == author) book.authors
end
booksByAuthor :: Author -> [Book] -> [Book]
booksByAuthor author books = do
findAuthor book = book.hasAuthor author
filter findAuthor books
end
end
module Library do
# A record is like a type and a module put together.
# You specify what fields a record has, complete with types.
record Author do
# This does two things:
# 1. Defines a `name` field for this record
# 2. Defines a getter with type Book -> String
fields do
name :: String
end
# Afterwards you can optionally specify a default implementation for `name`.
# That is, when we create an ad-hoc record that mixes in Author,
# it will receive everything explicitly defined in the record.
#
# Examples:
# author = { Author | name: "Alice", otherStuff: "yum" }
# author.name
# => "Alice"
# author.otherStuff
# => unknown function Author.otherStuff
#
# Note that record fields are not directly accessible. To get the value of
# a record field, you must deconstruct and return that field with a function,
# which brings us to the next line.
#
# In this next line, the brackets after `name` is deconstructing the first parameter,
# which is a record. Note that what's written here is the default implementation we would get if
# we did not define anything for `name`. In other words, the default is a getter.
name :: Book -> String
name { author: authorName } = authorName
end
record Genre do
# Remember the default getter? This record has equivalent functionality to Author.
# Note that we can also call a getter a record method. In this case,
# we can say we're defining the `name` method on the `Genre` record.
fields do
name :: String
end
# There are two ways to access record methods:
#
# 1. Explicitly. You can do this from anywhere, and on potentially different record types:
#
# getName :: Genre -> String
# getName genre = Genre.name genre
#
# 2. With inference. You can only do this when your variable is explicitly typed:
#
# getName :: Genre -> String
# getName genre = genre.name
#
# In these examples, the latter is equivalent to the former because in the latter we
# know the type of `genre` - it gets translated to `Genre.name genre` during compilation.
end
record Book do
fields do
title :: String
genre :: Genre
# Below is a default value for a field. Default values only get created when
# the record *type* is mixed into an ad-hoc record without the defaulted field specified.
authors = [] :: [Author]
# For example:
#
# author = { Author | title: "T", genre: { name: "G" } }
#
# This example type checks, since the `authors` field has a default value. However:
#
# author = { Author | title: "T" }
#
# This will *not* typecheck, since `genre` is without a value.
end
# A custom method on the Book record.
# Remember that you can call this via `Book.hasAuthor someBook a` or `someBook.hasAuthor a`
hasAuthor :: Book -> Author -> Bool
hasAuthor book author = any (\a a.name == author) book.authors
#
# Here's what the compiler will translate the above definition to:
#
# hasAuthor book author = any (\a a.name == author) book.authors
# hasAuthor book author = any (\a (Author.name a) == author) book.authors
# hasAuthor book author = any (\a (Author.name a) == author) (Book.authors book)
#
# This is possible because we know the types of both `book` and `author`.
end
booksByAuthor :: Author -> [Book] -> [Book]
booksByAuthor author books = do
findAuthor book = book.hasAuthor author
filter findAuthor books
end
# Alternatively:
# booksByAuthor :: Author -> [Book] -> [Book]
# booksByAuthor author books = do
# filter findAuthor books
# where
# findAuthor book = book.hasAuthor author
# end
end
# How to use a module:
# using Library
#
# jazz :: Library.Genre
# jazz = { name: "Jazz" }
#
# createLibrary :: [Library.Book]
# createLibrary = [{ Library.Book | title: "How to Jam", genre: jazz, authors: ["Johnny Davetrane"] }]
#
# You can be a lazy typer:
# using Library as L
#
# Or include types and functions into your scope:
# import Library (Genre, Book, booksByAuthor)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment