OCAML has several special features that make it unique. Three particular aspects of OCAML stuck out to me as important to iterate on. First aspect is REPL, which is a feedback loop. Second aspect is the principal option and principal type functionality. The last aspect of OCAML that stuck out to me was the pattern matching function.
The REPL is a feedback loop which facilitates a style of development and code exploration. But it isn't just a normal process; REPL is a far more engaging and effective process than the processes of writing and reading code would be without it. REPL is by far more superior to anything available that an IDE could provide. It is said that "A REPL is, in some sense, a simple but highly effective IDE. Properly built, it makes IDEs unnecessary" (Church, Michael). REPL is a useful technical aspect of OCAML because "REPLs make it possible to explore average-case code and comprehend it without dedicating massive amounts of time to the process, and this makes a huge difference. Aging modules can be refactored in the earliest stages of decay, long before they get anywhere near the "legacy horror" state" (Church, Michael). This enables interactions of functions. REPLs also allow programmers to explore libraries and get a sense of their interfaces to be used.
REPL is also useful because programmer's productivity in general is binary. Meaning, that a programmer is either in a productive, engaged state of "flow" or in a disorganized and unproductive state. "In general it takes programmers about 15 to 30 minutes to enter flow, but once in it, they are immensely productive and moreover, quite happy" (Church, Michael). Usually in the real world, developers are usually the ones having to write the most code and always write the best code when in flow. Developers are also best at reading code when in this state as well. However most will quickly agree that a major problem is in reading other people's code. If the code presents a lot of complex and obscured code, the question the reader is trying to answer often shatters the flow. OCAML and its REPL function have solved this issue completely.
Programmers will understand and agree that flow is importance from experience, and flow becomes more important as one increases one's programming skills. Programmers will negotiate perks oriented toward flow and engagement, such as a quiet working space and an unconditional right to turn down meetings instead of higher salaries so they may achieve maximum potential. "An experienced programmer can work at a 1.0 level (cranking out code of nominal additive value) without inspiration, but 1.5+ and especially 2.0+ level contributions require creativity and focus" (Church, Michael). With REPL, programmers could write amazing code and not have to be cut off from the modern world. "Experienced, elite programmers know that a REPL-less language is generally a dead-end. In a "green field" environment where the programmer controls the entire context in which his work exists, engaged writing of code in languages like Java is still possible- but engaged reading of code is out of the question" (Church, Michael). The engaging way to read code is to get the big-picture of it through interaction, and then examination of the code for implementation details after it has been achieved
The REPL is most commonly used at the commandline. This allows a person to interact with code as if it were live, and see what the pieces do. At least half of what programmers must do is comprehension of assets that other people have created. REPL allows us to do this without context switch associated with having to read obscure code. "It enables "flowful", that is engaging exploration and later; "lazy reading" of code. (Lazy, in this sense, is a non-pejorative computer science term associated with doing only the work needed to solve a problem.)" (Church, Michael). Everything stated above brings a conclusion that REPL-less languages can't provide due to the lack of code that is seen as a dead static thing that might be run against some dead static tests. This is not something a developer/programmer can interact with as he works.
"For example, in Ocaml, it's possible to get the full type signature of any module:
# module L = List;;
module L :
val length : 'a list -> int
val hd : 'a list -> 'a
val tl : 'a list -> 'a list
val nth : 'a list -> int -> 'a
val rev : 'a list -> 'a list
val append : 'a list -> 'a list -> 'a list
val rev_append : 'a list -> 'a list -> 'a list
val concat : 'a list list -> 'a list
val flatten : 'a list list -> 'a list
val iter : ('a -> unit) -> 'a list -> unit
val map : ('a -> 'b) -> 'a list -> 'b list
A programmer would know that it should look like thisâ€¦
# List.length [1; 1; 2; 3; 5; 8];;
- : int = 6
# List.map (fun x -> x*x) [1; 1; 2; 3; 5; 8];;
- : int list = [1; 1; 4; 9; 25; 64]"(Church, Michael).
A person can explore code and get a sense of the big picture before starting to read it. This makes a huge difference; reading code is an order of magnitude which is easier and more engaging when one understands what one is looking at.
Another interesting aspect of OCAML is the principal option/principal type. Given a program, a type for this program is principal if it is the most general type that can be given to the given program. This is in the sense that all other possible types are specialization or instances of this type. "For example, the program fun x -> x can be given the types int -> int and bool -> bool. with the given program, the polymorphic 'a -> 'a is a principal type" (http://stackoverflow.com/questions/11542446/what-is-a-principal-type). An interesting fact about principal types is that a good number of type systems use it. However some type systems, principal types do not always exist. You have a program P with two possible types T1 and T2, none of them being more general than the other. For example, "in some systems where numeric operators are overloaded, the program fun x -> x + x can be given the type int -> int and the type float -> float, and there is no type that subsumes both" (What Is A Principal Type?). This is a problem for the inference engine. It is a problem for the inference engine because it means that it has to make an arbitrary choice which allows it to pick one of the possible types without knowing if it's the one the user intended for. If you have principal types in your system/code, the inference process does not need to make any choice. Another example of principal option and principal types;
"A class is a model for objects. For instance, a class counter can be defined as follows.
class counter = object val mutable n = 0 method incr = n < - n + 1 method get = n end;;" (What Is A Principal Type?).
Objects of the class counter have a mutable field n and two methods incr and get. The field n in this class is used to record the current value of the counter and is initialized to 0. The two methods are used to increment the counter and read current.
As for any other declaration, the OCaml system infers a principal type for this declaration:
"class counter: object val mutable n: int method get: int method incr : unit end" (What Is A Principal Type?).
In the modern world, a large part of the designers of programming languages have given up on the idea of principality. The reason is because they want to have more ambitious type systems where it is just too hard to seek principality. "Instead non-principal inferences are being used: it's already good if the inference engine can find some type" (What Is A Principal Type?). However the primary maintainer of the OCaml type systems, still cares about it very much, which makes it an interesting aspect of the OCaml programming language.
The third technical aspect of OCAML I thought was interesting is the pattern matching. It is similar to variant types but the ability to pattern match over them. For pattern matching to happen it needs two basic syntaxes which OCAML provides. The matchâ€¦ with construct and the functionâ€¦ construct is the two basic syntaxes for pattern matching. The match ... with construct evaluates the given expression and then compares the result with a sequence of pattern matches:"
match expression with
| pattern -> ...
| pattern -> ...
| pattern -> ...
The expression corresponding to the first pattern that matches is then evaluated and returned" (Introducing OCAML).
The pattern aspect of the match patterns contain primitive values, tuples, records, variant type constructors, variable names and a catchall pattern denoted _ that matches any value. Sub patterns may contain alternatives, denoted pat1 | pat2. For example, the pattern 1 | 2 matches both 1 and 2. Another example where a function to invert a given state may be written:
"# let invert state =
match state with
| On -> Off
| Off -> On;;
val invert : state -> state = <fun>"( Introducing OCAML).
Here is an example where the context of the variant type representing a symbolic expression is tested where the following pattern match tests whether the expression e is an atom or a sum/product.
"match e with
| Int _ | Var _ -> true
| Add _ | Mul _ -> false
For example, Int 7 is an atomic expression:
# match Int 7 with
| Int _ | Var _ -> true
| Add _ | Mul _ -> false;;
- : bool = true" (Introducing OCAML).
Pattern matching is a vital aspect of the OCAML language. It is vital to the OCAML language because it underpins its expressive power. This expressive power that the pattern matching is what makes the OCAML language nice and user friendly.
OCAML is an interesting language with unique functions and features. These functions include the feedback loop (REPL), which makes it easier on programmers/developers. Other functions/features include the principal type and pattern matching which not only makes coding a lot easier but allows OCAML be an enjoyable and user friendly language.