comment 0

OCaml : Concepts – Part 3.1

Keeping the tradition ! Hello World!

Open up ocaml console and do the following

[siddhant:~] ocaml
        OCaml version 4.03.0

 print_string "Hello world!\n";;
Hello world!
- : unit = ()
#
Hello world!
– : unit = ()

It Works!

Great! Enough fun!  Now let’s get serious.

Things to note:
  • Things to note: Henceforth we will be using utop, which is an improved version of the default ocaml standard top level console.  utop supports line edition, history, real-time and context-sensitive completion, colors, and more. To install utop use
    opam install utop
  • We will also be using Core, a more full-featured and capable replacement for OCaml’s standard library. To install this use
    opam install core
  • Though opam has installed the core module, we need to include it in the .ocamlinit file
  • add the following lines to your ~/.ocamlinit file
    #use "topfind";;
    #thread;;
    #camlp4o;;
    #require "core.top";;
    #require "core.syntax";;
  • Let’s start by opening up utop

screen-shot-2016-11-17-at-11-29-36-pm

  • Now lets import core
    # open Core.Std;;

Basics

─( 23:50:06 )─< command 0 >─────────────────────────────────────────{ counter: 0 }
─utop # open Core.Std;;
─( 23:50:07 )─< command 1 >─────────────────────────────────────────{ counter: 0 }
─utop # 9 + 9;;
- : int = 18
─( 23:50:22 )─< command 2 >─────────────────────────────────────────{ counter: 0 }
─utop # 10 / 3;;
- : int = 3 
─( 23:50:33 )─< command 3 >─────────────────────────────────────────{ counter: 0 }
─utop # 5.5 + 6.5;;
Error: This expression has type float but an expression was expected of type int
─( 23:51:11 )─< command 4 >─────────────────────────────────────────{ counter: 0 }
─utop # 5.5 +. 6.5;;
- : float = 12.
─( 23:51:52 )─< command 5 >─────────────────────────────────────────{ counter: 0 }
─utop # 5.5 +. 6;;
Error: This expression has type int but an expression was expected of type float
─( 23:52:05 )─< command 6 >─────────────────────────────────────────{ counter: 0 }
─utop # 100_000 + 25;;
- : int = 100025 
─( 23:52:21 )─< command 7 >─────────────────────────────────────────{ counter: 0 }
─utop # sqrt 9.;;
- : float = 3.
  • Notice the ‘;;’. It pissed me off to type semi-colon twice, but, this is required to tell the top level to evaluate an expression. Well, what is top level? The top level is the interactive mode that we are using (ocamel / utop). In the other chapters while we use the non interactive mode and use files to store the code, we can use a single semi-colon and OCaml will be happy!
  • If you have noticed, top level first prints the TYPE of the result followed by the evaluation.
  • ‘+’ is used for int and ‘+.’ is used for float operations.
  • Doesn’t type cast automatically. 5.3 +. 6 is an error. This is because ocaml is a type-safe language. It enforces this to avoid accidental conversions.
  • Numbers are more readable, Hundred thousand (100,000) can be written as 100_000. Compiler will neglect the underscores in the number. This is just for making long numbers readable
  • sqrt is a function. Unlike java, arguments are passed via spaces ( like the poetry mode in ruby) and not using parenthesis sqrt(9).
Variables
  • Variables are initialized using the let keyword. Variables should always start with lower case or underscore. punctuation ‘ is allowed in the variable name.
  • # let x = 10;;
    val x : int = 10
    # let x' = -10 ;;
    val x' : int = -10
    # let _y = 98.7 ;;
    val _y : float = 98.7
Functions
  • Functions are also defined by the let keyword
  • # let square x = x * x ;;
    val square : int -> int = <fun>
    # square 2;;
    - : int = 4
    # square (square 2);;
    - : int = 16
  • # let sum_if_true test first second =
        (if test first then first else 0)
        + (if test second then second else 0)
      ;;
    val sum_if_true : (int -> bool) -> int -> int -> int = <fun>
  • The first argument (lsum_if_true) is the name of the function, test is a function itself that is passed as a parameter, first and second are variables.
  • # let even x =
        x mod 2 = 0 ;;
    val even : int -> bool = <fun>
    # sum_if_true even 3 4;;
    - : int = 4
    # sum_if_true even 2 4;;
    - : int = 6
  • WHOOA! What just happened!? Lets go step by step, even is function that takes an int and returns a bool. Now we are passing the function even to sum_if_true. So for it, parameters,  test = function even, first =3 and second = 4.
  • Visualize this like, if (even 3) then 3 else 0 + if(even 4) then 4 else 0 . Which evaluates to 0 + 4.
  • NOTE: Howdy, Java and C++ programmers, did you notice something? In let even x =x mod 2 = 0 ;; the operator ‘=’ is used both to assign and compare, unlikle == in Java. ‘=’ has different meanings depending on where it is uses. Gotcha be careful!

 Tuples

  • You can create a tuple by joining values with a comma.
  • Tuples CAN have data of two different kinds. However once a tuple is created you cannot add more variables to it.
  • This is in contrast to lists where you can add more values at any point of time. But lists can NOT have data of more than one kind.
  • # let my_tuple = (9, "sidx" 10.0) ;;
    val my_tuple : int * string * float = (9, "sidx", 10.)
  • You can extract values of a tuple by using OCaml pattern matching.
    let (a, b, c) = my_tuple;;
    val a : int = 9 val b : string = "sidx" val c : float = 10.
  • And you can do amazing things like
  • let distance (x1,y1) (x2,y2) =
        sqrt ((x1 -. x2) ** 2. +. (y1 -. y2) ** 2.)
      ;;
    val distance : float * float -> float * float -> float = <fun>
    
    let d1 = (10. ,5.);;
    val d1 : float * float = (10., 5.)
    
    let d2 = (9.0, 6.0);;
    val d2 : float * float = (9., 6.)
    
    distance d1 d2;;
    - : float = 1.41421356237
  • Blown off? Yeah it’s powerful!

Lists

  • Lists can hold any number of items of the same type.
  • let languages = "English" :: "Spanish" :: "French" ::[] ;;
    val languages : string list = ["English"; "Spanish"; "French"]
  • we can do certain operations like List.length languages;;
    List.map languages String.length;;
  • Note that, list items are separated using ‘;’ (semi-colon). And this is important. Separating them with comma will make it behave as a list of tuples and will have undesirable results.
  • let my_favorite_language languages =
     match languages with
     | first :: the_rest -> first
     | [] -> "OCaml" (* A good default! *)
     ;;
    val my_favorite_language : string list -> string = <fun>
    # my_favorite_language ["English";"Spanish";"French"];;
    - : string = "English"
    # my_favorite_language [];;
    - : string = "OCaml"
  • You can match patterns with list.
    first::the_rest – This splits the list into two parts, the first element is assigned to first and the rest of the list to the_rest.
    Similarly, we could have split it into three parts by first::second::rest, first,second are just names, you can write anything like a:b:my_rest.
  • ‘|’ is used as an or, and ‘->’ returns the value
  • [] matches with an empty string.

Recursion

  • Recursions are the functions that call themselves. We have base case and the  inductive case. Base case is the stopping condition. Inductive case is the one that calls the function from within the function.
  • # let rec sum l =
     match l with
     | [] -> 0 (* base case *)
     | hd :: tl -> hd + sum tl (* inductive case *)
     ;;
    val sum : int list -> int = <fun>
    # sum [1;2;3];;
    - : int = 6
  • Here’s a function for removing sequential duplicates:
  • let rec destutter list =
     match list with
     | [] -> []
     | [hd] -> [hd]
     | hd1 :: hd2 :: tl ->
     if hd1 = hd2 then destutter (hd2 :: tl)
     else hd1 :: destutter (hd2 :: tl)
     ;;
    val destutter : 'a list -> 'a list = <fun>
    # destutter ["hey";"hey";"hey";"man!"];;
    - : string list = ["hey"; "man!"]

Options

  • An option is used to express that a value might or might not be present.
  • let divide x y =
        if y = 0 then None else Some (x/y) ;;
    val divide : int -> int -> int option = <fun>
  • # let log_entry maybe_time message =
        let time =
          match maybe_time with
          | Some x -> x
          | None -> Time.now ()
        in
        Time.to_sec_string time ^ " -- " ^ message
      ;;
    val log_entry : Time.t option -> string -> string = <fun>
    # log_entry (Some Time.epoch) "A long long time ago";;
    - : string = "1969-12-31 19:00:00 -- A long long time ago"
    # log_entry None "Up to the minute";;
    - : string = "2013-11-05 08:47:56 -- Up to the minute"
  • Concept Alert!
    Nesting lets with let and in
    let x = 7 in
     x + x
     ;;
    - : int = 14
  • The in keyword describes the scope of x. In other words x is only available inside the space after in till ;;. Any reference to x outside that is not valid.

Creating our own Data Type

  • We can create our own data structure by using the ‘type’ keyword.

    type point2d = { x : float; y : float };;
    type point2d = { x : float; y : float; }
    
    let p = { x = 3.; y = -4. };;
    val p : point2d = {x = 3.; y = -4.}

 

Leave a Reply

Your email address will not be published. Required fields are marked *