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

feat: adding solutions #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
npm-debug.log
/lib/bs/
/node_modules/
*.bs.js
*.bs.js
.vscode/
37 changes: 37 additions & 0 deletions src/solutions/01-introduction/introduction.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Hello,

Reason is not a new language. It is syntactic sugar for the OCaml language
created in 1996. The new Reason syntax will look familiar to those who have
programmed in Javascript.

These challenges are originally from the workshop run by Jane Street to
teach OCaml. It was ported over to Reason with the help of `refmt` (Reason
format) binary that helps you convert to/from Reason/OCaml syntax.

This exercise is to familiarize you with the code-compile cycle.

To compile your code, run in a terminal session in the root directory

$ npm run build

You should see a compilation error because it's missing the end quote. Add
the end quote and re-run. You should see that the code compiled successfully!

For convenience you can build whenever you change the code by running the
compiler in watch mode. For that, run

$ npm run start

You can also execute code in Reason's REPL (rtop) directly. To start rtop from
your terminal run:

$ rtop

Now try pasting the code below into it.

*Note:* Use `Ctrl-d` to exit the rtop session.

Try these out and move on to the next exercise!
*/
let () = print_endline("Hello, World!");
159 changes: 159 additions & 0 deletions src/solutions/02-basic_types/basicTypes.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
In Reason there are 6 basic types: int, float, char, string, bool, and unit.

The following exercises and examples will give you a brief introduction to
these types. Feel free to play around with them in rtop.

Note the keyword [let], which is how variable assignment is done in Reason.

In Reason floats are distinguished from ints by their decimal points. 0 is an
int, 0. is a float.

In addition the basic math operations are also distinguished by a decimal
point. For example, + allows you to add two ints and +. allows you to add two
floats.

Signatures
==========

four is a value with the type int. We write the signature like this:

let four : int

Read it like this: "[four] is a value of type int".

Signatures are similar to type declarations in other languages. They tell the
compiler (and human readers of your code!) the types of variables and
functions in your program. For example, in C, C++, and Java, the signature
above would be written like so:

int four;
*/
let four = 4;

/*
floatFour is a value with the type float. We write the signature like this:

let floatFour : float

You may have noticed that the two signatures we showed you were in comments.
Signatures are not always required! In many situations, you may omit them,
and the compiler will infer the type of values.

However, if you do write a signature for a value, the compiler will make sure
to check that it's consistent with how that value is used.

Try inserting an incorrect signature for [floatFour] to see what error the
compiler gives you.
*/
let floatFour = 4.;

/*
Function signatures
===================

In Reason, functions are also values! And so, functions also have type
signatures.

In a function signature, types of parameters are enclosed within parentheses.
The return value is the described last and preceded immediately by an arrow
[=>].

So the signature for a function that takes two integers and returns an
integer is:

let intAverage: (int, int) => int

In Reason there's no explicit return statement: functions just return the
value of the last statement in that function.
*/
/* let intAverage = (x, y) => failwith("For you to implement"); */
let intAverage = (x, y) => (x + y) / 2;

/* let floatAverage : (float, float) => float */
/* let floatAverage = (x, y) => failwith("For you to implement"); */
let floatAverage = (x, y) => (x +. y) /. 2.0;

/*
The following expression computes the average of 10 and 20:

intAverage(10, 20)

As in many languages strings are denoted with "" and chars are denoted with ''.

String concatenation is done with the ++ operator.

let firstName : string
*/
let firstName = "Fred";

/* You can also write type annotations in definitions */
let lastName: string = "Flintstone";

/*
But Reason has very strong type inference, so you can most often omit types,
and the compiler can infer that fullName is a string.
*/
let fullName = firstName ++ " " ++ lastName;

let aBooleanFalse: bool = false;

/*
You can use
&& for logical and
|| for logical or
! for logical not
*/
let () = assert (true || aBooleanFalse);

/*
The [unit] type
===============

unit is a special type in Reason that has only one possible value written ().
It is generally used for mutation and io-operations such as printing.

(I/O stands for input/output. Examples: printing to screen, reading a file,
sending and receiving network requests.)

To combine several unit operations together the ; operator is used contained
within curly braces.
*/
let () = {
print_endline("Hi, My name is ");
print_endline(fullName);
print_endline(" and I am 5 years old");
};

/*
The lines that follow are inline tests. Each evaluates a boolean expression.
They are run during the build, and failures -- evaluating to false -- are
treated like compile errors by the build tool and editors.

We will see other kinds of inline tests later, and some interesting patterns
for using them.
While Reason supports polymorphic comparison, it is good practice to use
equality and comparison functions specific to each type.

So, [Int.equal] is the [equal] function defined in the [Int] module. Its
signature is

val equal : int -> int -> bool

In words: [equal] takes two [int]s and returns a [bool]. The following line
is applying that function to two inputs, [5] and [int_average 5 5].
*/
Test.runAll([
(intAverage(5, 5) == 5, "int average"),
(floatAverage(5., 5.) == 5., "float average"),
(floatAverage(5., 10.) == 7.5, "float average"),
]);
/*
.rei files
==========

Check out the [basicTypes.rei] file in this directory! It declares the types for
the two functions you had to implement. If the types in the [.rei] don't
match the types of the values in the [.re], the compiler will flag that as an
error.
*/
12 changes: 12 additions & 0 deletions src/solutions/02-basic_types/basicTypes.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
This is an rei, a file that declares the interface that the corresponding
implementation file (basicTypes.re) exposes to other code.

The compiler will enforce that the implementations you write for
[intAverage] and [floatAverage] in basicTypes.re have the type signatures
written below.

*/
let intAverage: (int, int) => int;

let floatAverage: (float, float) => float;
33 changes: 33 additions & 0 deletions src/solutions/03-define_functions/defineFunctions.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
We use let to define functions.

Definitions take on the form:
let functionName = (arg1, arg2) => body;

For example, here we define a function add1 that takes a single int
argument and returns that argument plus 1.
*/
let add1 = arg => arg + 1;

/* This function uses the built-in ++ operator to append strings. */
let stringAppend = (x, y) => x ++ y;

/* Let's define our own functions using +, -, *, and / below. */
let plus = (x, y) => x+y;

let times = (x, y) => x*y;

let minus = (x, y) => x-y;

let divide = (x, y) => x/y;

Test.runAll([
(plus(1, 1) == 2, "plus"),
(plus(50, -1) == 49, "plus"),
(times(8, 8) == 64, "times"),
(times(2, -1024) == (-2048), "times"),
(minus(-2, 2) == (-4), "minus"),
(minus(1337, 337) == 1000, "minus"),
(divide(1024, 2) == 512, "divide"),
(divide(31337, 31) == 1010, "divide"),
]);
7 changes: 7 additions & 0 deletions src/solutions/03-define_functions/defineFunctions.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let plus: (int, int) => int;

let times: (int, int) => int;

let minus: (int, int) => int;

let divide: (int, int) => int;
26 changes: 26 additions & 0 deletions src/solutions/04-call_functions/callFunctions.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* Here are some example functions: */
let square = x => x * x;

let half = x => x / 2;

let add = (x, y) => x + y;

/* You can order function invocations with parentheses or let bindings */
/* Parens */
let () = Js.log("(5^2)/2 = " ++ string_of_int(half(square(5))));

/* Let bindings */
let () = {
let squared = square(5);
let halved = half(squared);
let toString = string_of_int(halved);
Js.log("(5^2)/2 = " ++ toString);
};

/* Try to write [average] by reusing [add] and [half] */
let average = (x, y) => half(add(x,y));

Test.runAll([
(average(5, 5) == 5, "average"),
(average(50, 100) == 75, "average"),
]);
1 change: 1 addition & 0 deletions src/solutions/04-call_functions/callFunctions.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let average: (int, int) => int;
38 changes: 38 additions & 0 deletions src/solutions/05-twice/twice.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
We can easily write a function that adds 1 to any number.
Recall that the infix operator (+) will add two integers.
*/
let add1 = x => x+1;

/*
Let's write a function that squares its argument (multiplies it by itself)
*/
let square = x => x*x;

/*
Functions are first class in Reason. This means that you can take
a function and pass it around as an argument to other functions.

Let's write a function named twice: it will take a function and apply
that function to itself two times.

For example, if we wanted to make an "add2" function, we could do it
by writing:
let add2 = twice(add1)
*/
let twice = (f, x) => f(f(x));

/* Now that we have twice, write add2 and raiseToTheFourth */
let add2 = (x) => twice(add1, x); /* Hint: use add1 */

let raiseToTheFourth = (x) => twice(square,x); /* Hint: use square */

Test.runAll([
(add1(4) == 5, "add1"),
(square(4) == 16, "square"),
(square(-4) == 16, "square"),
(twice(add1, 3) == 5, "twice"),
(add2(1335) == 1337, "add2"),
(raiseToTheFourth(1) == 1, "raiseToTheFourth"),
(raiseToTheFourth(10) == 10000, "raiseToTheFourth"),
]);
9 changes: 9 additions & 0 deletions src/solutions/05-twice/twice.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
let add1: int => int;

let square: int => int;

let twice: ('a => 'a, 'a) => 'a;

let add2: int => int;

let raiseToTheFourth: int => int;
26 changes: 26 additions & 0 deletions src/solutions/06-pattern-matching/patternMatching.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Pattern matching lets us compare inputs to known values.
Patterns following "|" are tested in order.
On the first match, we use the result following "=>".
The "_" pattern means "could be anything".
*/
let isSuperman = x =>
switch (x) {
| "Clark Kent" => true
| _ => false
};

/*
Let's use our own pattern matching. Write a function that returns
whether x is non zero by matching on x
*/
let nonZero = x => switch(x) {
| 0 => false
| _ => true
};

Test.runAll([
(nonZero(0) == false, "non zero"),
(nonZero(500) == true, "non zero"),
(nonZero(-400) == true, "non zero"),
]);
1 change: 1 addition & 0 deletions src/solutions/06-pattern-matching/patternMatching.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let nonZero: int => bool;
Loading