Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Option to stdlib #158

Merged
merged 2 commits into from
Jun 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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