For what it’s worth, I haven’t seen anyone else suggest what I normally do, which is set up all my definitions, then set up a
main function for things which have side effects, then call the
let a = ref 0
let b = ref 0
let f x = x + !a
let g x = x + !b
let c x = f x + g x
let main () =
a := 1;
b := 1;
Printf.printf "%d\n" (c 10);
a := 2;
b := 2;
Printf.printf "%d\n" (c 10)
let () = main ()
With a little practice, it’s fairly easy to tell what has side effects–usually any expression that returns
a := 1. These can be combined together with
Finally, you run the
main function at the bottom to actually carry out all the side effects.
That’s a good paradigm. I might not use the correct wordings but “Isolation” of the “functional” and “non-functional” parts of code.
Following compiles fine. I post it solely for demonstration purpose.
let _ =
let z = 1 in
let a = ref 2 in
let b = ref 3 in
let f x = x + !a in
let g (x : int) : int = x + !b in
let c f g x = f x + g x + z in
let printd d = Printf.printf "%d\n" d in
let mutation () =
let () = a := 1 in
let () = (b := 1 : unit) in
let () =
(let () = (a := 4 : unit) in
(b := !a + 5 : unit)
let () = printd (c f g 10) in
TL;DR OCaml is very much an expression language.
;; is only used in the repl to evaluate things since line breaks don’t matter.
expr1 ; expr2 is a binary operator expression. it has associativity and precedence so you may need to use parens (or
begin..end which are generally seen as more verbose parens) in complicated expressions which may end up stealing values from
an example of that is the
if .. then .. else ternary expression.
let has two forms: a toplevel binding, and an expression that evaluates to something.
let name = expr is the toplevel binding.
let name = expr1 in expr2 name evaluates
expr1, binds it to
name, and uses
name in evaluating
in doesn’t end stuff, it’s just part of the syntax for separating expressions.
since the second form is an expression, it can be used in the first form:
let (* toplevel binding*) eight = let (* expression *) five = 5 in y + 3
I intentionally put them on the same line to show line breaks don’t really do anything in OCaml.
conveniently, the way
let expression syntax is made allows you to nest or use it wherever an expression is expected:
let _ = let y = let x = 5 in x in let z = y in print_int z
let _ = (let y = (let x = 5 in x) in (let z = y in print_int z))
If you want to write standalone expressions and evaluate them without binding them to something, you may use
;; like you would in the repl, to force an evaluation… but it’s not something you see often if at all in source files. Just in tutorials because they expect you to follow along in the repl.
So no rules of thumb, just a different way to parse things that’s quite consistent once you’re aware of it.
I’m a little late to the party, but if you can already think in terms of scheme expressions, here are some rough translations from Scheme to OCaml. Mostly posting this because I don’t like some of the above RTFM responses and found them a little hostile.
You can often omit the outer parens, so most of the time it’ll look like the following.
(let ((foo expr1)
let foo = expr1 in
let bar = expr2 in
let baz = expr3 in
(letrec ((foo expr1)
foo = expr1 and
bar = expr2 and
baz = expr3
(define (foo arg1 arg2) ...)
(define bar ...)
(define baz ...)
let foo arg1 arg2 = ...
let bar = ...
let baz = ...
The thing to note here is that there is no
in between these expressions, so they are separate expressions, not one large expression. (This statement/translation is rough and comes with caveats. See manual for parsing details.)
The repl doesn’t know when the expression ends unless you do “
;;”. In the example, after the first line, the repl doesn’t know if you wanted to end the expression or not. The expression ends after the “
+ 2” here, and you have to manually inform the repl that you are done inputting text. Note: I put the closing bracket and “
;;” on separate lines for demonstration, but you’d normally just write “
2)” or “
OT, but this reminded of the old camlp4/5 scheme syntax Scheme.
[I can use racket editor to write ocaml code]
What is nice about scheme is that it’s clear where something ends and also which things stay together.
I don’t know yet effect on operator “priority”
Alain, perhaps you’re referring to the Scheme syntax parser that comes with Camlp5 ? Two thoughts:
- the Scheme support in Camlp5 has no users that I know of. If you wanted to use it, I’d be happy to support you, but I would rely on your knowledge of Scheme, if there were discrepancies/bugs. B/c I don’t remember my Scheme (it was 30+ years ago, after all).
@Jason_Nielsen recently released a scheme front-end, and you might want to consider that, simply because he’s also using it, so you wouldn’t be alone.
I’m not trying to dissuade you; just to make sure that you’re aware of what you’re getting into.
OK, now I’ll try to dissuade you. I think it’s great to use OCaml in Scheme syntax mode, if you already know Ocaml well. But if you don’t (and from your posts, I think I’m not going out on a limb to assess that you’re pretty new to OCaml) then you might find it difficult, and more importantly, when there are things that don’t work as you expect, you’ll be constantly having to decide “which of OCaml and the Camlp5/Scheme front-end is behaving in a manner I don’t expect?”
It could get unpleasant.
Yes. I’ll try it temporary and transitional. To go from one to the other.
But extension of syntax is an interesting concept.
[ PS: In racket scheme i could do & object-orientation & type-annotation & infix-operators together.]