Skip to content

Commit

Permalink
Merge pull request #158 from phated/stdlib-option
Browse files Browse the repository at this point in the history
Add Option to stdlib
  • Loading branch information
ospencer committed Jun 28, 2020
2 parents f516b45 + a0621ab commit 864e3a6
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 1 deletion.
23 changes: 22 additions & 1 deletion compiler/src/utils/warnings.ml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ type state = {
}

let current = ref {
active = Array.make (last_warning_number + 1) true;
active = Array.make (last_warning_number + 1) false;
error = Array.make (last_warning_number + 1) false;
}

Expand All @@ -107,6 +107,27 @@ let is_error x = (!current).error.(number x)

let nerrors = ref 0

let defaults = [
LetRecNonFunction "";
AmbiguousName ([], [], false);
NotPrincipal "";
NameOutOfScope ("", [], false);
StatementType;
NonreturningStatement;
AllClausesGuarded;
PartialMatch "";
UnusedMatch;
UnusedPat;
NonClosedRecordPattern "";
UnreachableCase;
ShadowConstructor "";
NoCmiFile ("", None);
]

let _ = List.iter (fun x ->
(!current).active.(number x) <- true
) defaults

type reporting_information = {
number : int;
message : string;
Expand Down
126 changes: 126 additions & 0 deletions compiler/test/stdlib/option.test.gr
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import Option from 'option'
import String from 'strings'

# Option.isSome

assert Option.isSome(Some(1)) == true
assert Option.isSome(Some('🌾')) == true
assert Option.isSome(None) == false

# Option.isNone

assert Option.isNone(None) == true
assert Option.isNone(Some(1)) == false
assert Option.isNone(Some('🌾')) == false

# Option.contains

assert Option.contains(1, Some(1)) == true
assert Option.contains(2, Some(1)) == false
assert Option.contains('🌾', Some('🌾')) == true
assert Option.contains('🌾', Some('🐑')) == false
assert Option.contains('🌾', None) == false

# Option.expect

assert Option.expect('Fails with this message if none', Some(1)) == 1
assert Option.expect('Fails with this message if none', Some('🌾')) == '🌾'
# TODO: Test this when we can
# assert Option.expect('Failed with this message', None)

# Option.unwrap

assert Option.unwrap(Some(1)) == 1
assert Option.unwrap(Some('🌾')) == '🌾'
# TODO: Test this when we can
# assert Option.unwrap(None)

# Option.unwrapWithDefault

assert Option.unwrapWithDefault(2, Some(1)) == 1
assert Option.unwrapWithDefault('🐑', Some('🌾')) == '🌾'
assert Option.unwrapWithDefault(2, None) == 2
assert Option.unwrapWithDefault('🐑', None) == '🐑'

# Option.map

assert Option.map((x) => x * 2, Some(2)) == Some(4)
assert Option.map((x) => String.concat('hello ', x), Some('🌾')) == Some('hello 🌾')
assert Option.map((x) => fail 'Should not be called', None) == None

# Option.mapWithDefault

assert Option.mapWithDefault(1, (x) => x * 2, Some(2)) == 4
assert Option.mapWithDefault('🐑', (x) => String.concat('hello ', x), Some('🌾')) == 'hello 🌾'
assert Option.mapWithDefault(1, (x) => x * 2, None) == 1
assert Option.mapWithDefault('🐑', (x) => String.concat('hello ', x), None) == '🐑'

# Option.flatMap

assert Option.flatMap((x) => Some(x * 2), Some(2)) == Some(4)
assert Option.flatMap((x) => Some(String.concat('hello ', x)), Some('🌾')) == Some('hello 🌾')
assert Option.flatMap((x) => None, Some('🌾')) == None
assert Option.flatMap((x) => fail 'Should not be called', None) == None

# Option.filter

assert Option.filter((x) => x == 2, Some(2)) == Some(2)
assert Option.filter((x) => x == 2, Some(1)) == None
assert Option.filter((x) => fail 'Should not be called', None) == None

# Option.zip

assert Option.zip(Some(1), Some(2)) == Some((1, 2))
assert Option.zip(Some(1), Some('🌾')) == Some((1, '🌾'))
assert Option.zip(Some(1), None) == None
assert Option.zip(None, Some('🌾')) == None

# Option.zipWith

assert Option.zipWith((a, b) => a + b, Some(1), Some(2)) == Some(3)
assert Option.zipWith((a, b) => (a, b), Some(1), Some('🌾')) == Some((1, '🌾'))
assert Option.zipWith((a, b) => fail 'Should not be called',Some(1), None) == None
assert Option.zipWith((a, b) => fail 'Should not be called', None, Some('🌾')) == None

# Option.flatten

assert Option.flatten(Some(Some(1))) == Some(1)
assert Option.flatten(Some(None)) == None
assert Option.flatten(None) == None

# Option.toList

assert Option.toList(Some(1)) == [1]
assert Option.toList(Some('🌾')) == ['🌾']
assert Option.toList(None) == []

# Option.toArray

assert Option.toArray(Some(1)) == [> 1]
assert Option.toArray(Some('🌾')) == [> '🌾']
assert Option.toArray(None) == [>]

# Option.sideEffect

let a = box(2)

Option.sideEffect((x) => ignore(a *= x), Some(2))

assert ^a == 4

let b = box('🌾')

Option.sideEffect((x) => fail 'Should not be called', None)

assert ^b == '🌾'

# Option.peek

let c = box(2)

assert Option.peek((x) => ignore(c *= x), Some(2)) == Some(2)
assert ^c == 4

let d = box('🌾')
assert Option.peek((x) => fail 'Should not be called', None) == None
assert ^d == '🌾'
1 change: 1 addition & 0 deletions compiler/test/test_end_to_end.ml
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ let stdlib_tests = [

tlib "arrays.test";
tlib "lists.test";
tlib "option.test";
tlib "hash.test";
tlib "int64.test";
tlib "strings.test";
Expand Down
122 changes: 122 additions & 0 deletions stdlib/option.gr
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Standard library for list functionality

export *

let isSome = (opt) => {
match (opt) {
| Some(_) => true
| None => false
}
}

let isNone = (opt) => {
match (opt) {
| None => true
| Some(_) => false
}
}

let contains = (val, opt) => {
match (opt) {
| Some(x) => x == val
| None => false
}
}

let expect = (msg, opt) => {
match (opt) {
| Some(x) => x
| None => fail msg
}
}

let unwrap = (opt) => {
expect('Could not unwrap None value', opt)
}

let unwrapWithDefault = (default, opt) => {
match (opt) {
| Some(x) => x
| None => default
}
}

let map = (fn, opt) => {
match (opt) {
| Some(x) => Some(fn(x))
| None => None
}
}

let mapWithDefault = (default, fn, opt) => {
match (opt) {
| Some(x) => fn(x)
| None => default
}
}

let flatMap = (fn, opt) => {
match (opt) {
| Some(x) => fn(x)
| None => None
}
}

let filter = (pred, opt) => {
match (opt) {
| Some(x) =>
if (pred(x)) {
Some(x)
} else {
None
}
| None => None
}
}

let zip = (optA, optB) => {
match ((optA, optB)) {
| (Some(a), Some(b)) => Some((a, b))
| _ => None
}
}

let zipWith = (fn, optA, optB) => {
match ((optA, optB)) {
| (Some(a), Some(b)) => Some(fn(a, b))
| _ => None
}
}

let flatten = (opt) => {
match (opt) {
| Some(Some(x)) => Some(x)
| _ => None
}
}

let toList = (opt) => {
match (opt) {
| Some(x) => [x]
| None => []
}
}

let toArray = (opt) => {
match (opt) {
| Some(x) => [> x]
| None => [>]
}
}

let sideEffect = (fn, opt) => {
match (opt) {
| Some(x) => fn(x)
| None => void
}
}

let peek = (fn, opt) => {
sideEffect(fn, opt)
opt
}
3 changes: 3 additions & 0 deletions stdlib/pervasives.gr
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ primitive (is) : (a, a) -> Bool = "@eq"
import foreign wasm toString : a -> String from 'grainBuiltins'

export data List<a> = [] | [...](a, List<a>)

# Maybe some data, maybe not!
data Option<a> = Some(a) | None

0 comments on commit 864e3a6

Please sign in to comment.