Jump to content
  • Advertisement
Sign in to follow this  
Promit

Formatting O'Caml code

This topic is 4055 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm having considerable trouble applying my typical indenting behavior (or in fact, any indenting behavior) to O'Caml in a way that makes sense. Here's the snip I'm messing with:
let rec map_test f test l =
(
	match l with
		[] -> []
		| head :: tail -> [(if test head then f head else head)] @ (map_test f test tail)
);;

As is, it's already a mess, and I'm not sure what's traditional to make this look pretty and nice.

Share this post


Link to post
Share on other sites
Advertisement
Any reason why you're creating a list of one-element lists?

Either way, I would indent it as:

let rec map_test f test = function
| [] -> []
| h::t ->
(if test h then f h else h) :: (map_test f test t)


Or (but this is beyond the scope of the thread) :
let map_test f test =
List.map (fun x -> if test x then f x else x)


Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Any reason why you're creating a list of one-element lists?
I thought @ appended lists. What exactly is the difference between :: and @? Actually, I don't really understand why your code differs but does the same thing. "match" removed and just "function" there, for example.

Share this post


Link to post
Share on other sites
Quote:
Original post by Promit
I thought @ appended lists. What exactly is the difference between :: and @?


Disregard my comment, I had read "::" instead of "@" in your code. @ appends arbitrary lists (which means both lists are evaluated and concatenated in potentially linear time), while :: constructs a new list from a head and a tail (which is universally faster than concatenation, and draws an elegant parallel with pattern matching).

Also, [a] is syntactic sugar for a::[].

Quote:
Actually, I don't really understand why your code differs but does the same thing. "match" removed and just "function" there, for example.


let a x = match x with is equivalent with let a = function, so the second is usually more idiomatic because it's shorter. let a = fun is slightly different in its pattern matching potential (it can pattern-match several arguments at once).

Share this post


Link to post
Share on other sites
:: is the 'cons' operator and appends an element to the front of the list. @ joins two lists. In your case...I don't think there is much of a speed difference.

But normally, a @ b is essentially just:
a' = List.reverse(a), then a loop to :: each element in a' onto b.

...if I am not mistaken, which I may be. Either way, @ is 'slow'.

As for your indenting behavior ... well, I don't use OCaml, but SML is similar enough. As far as it goes, I don't see anything particularly wrong. Personally I just prefer to use 3 spaces so you don't get so much whitespace -- but you have the general idea in my opinion.

With most functional languages, I have generally found this statement to be true: if you code looks terse and ugly, there is probably a more elegant solution.

As Toohr points out, a List.map is much more elegant solution.


EDIT: Egads, Toohr beat me to a response. Ignore everything I say.

Share this post


Link to post
Share on other sites
:: creates a pair where the first element is the head of the list and the second is the tail, which is a list by itself. That is how lists are built in ml, ie. [1, 2, 3, 4] is actually 1 :: (2 :: (3 :: (4 :: NIL))).

So when you do x :: y the result is the list [x, y1, y2, y3, ..., yn] (x is anything and y is a list) whereas x @ y results in [x1, x2, ..., xn, y1, y2, y3, ..., yn]. The former is constant time O(1) where the later is linear time O(n).

Appending can be implemented by consing (::) all the elements of x on the list of y.

Share this post


Link to post
Share on other sites
Quote:
Original post by visage
But normally, a @ b is essentially just:
a' = List.reverse(a), then a loop to :: each element in a' onto b.


Functionally, in O'Caml, a @ b may be:

let prefix @ l1 l2 =
let rec aux = function
| [] -> l2
| h::t -> h :: (aux t)
in aux l1



It's not guaranteed to be tail-recursive.

An alternative is List.rev_append (List.rev a) b, which is tail-recursive but traverses the list twice.

Last but not least, it would theoretically be possible to implement concatenation as a very fast and constant-space native operation (by creating the list before creating its tail), but I don't think this is done.

Share this post


Link to post
Share on other sites
Quote:
Original post by visage
As Toohr points out, a List.map is much more elegant solution.
Yeah, but it's homework, so I don't get to use library functions whenever.

Share this post


Link to post
Share on other sites
Quote:
let a x = match x with is equivalent with let a = function, so the second is usually more idiomatic because it's shorter. let a = fun is slightly different in its pattern matching potential (it can pattern-match several arguments at once).


Interesting. I had thought the match with style was preferred because it is clearer the number of arguments required to fully apply the function. The let ... = function style requires that you look either side of the equals sign. On the other hand, I sometimes use the function style because the match with style results in a certain amount of local namespace pollution (i.e. you name something that is rarely used in the function body).

Something I've wondered, is there a concensus on when partially applied functions should be used? I tend too avoid using them to much since, to me at least, they tend to reduce readability (although they can be very useful in some situations). Is this idiomatic Ocaml?

Share this post


Link to post
Share on other sites
Quote:
Original post by Oralloy
Interesting. I had thought the match with style was preferred because it is clearer the number of arguments required to fully apply the function. The let ... = function style requires that you look either side of the equals sign.


The number of arguments is not something very important. First, it's nearly impossible to use an incorrect number of arguments in your program without the compiler complaining (the only ordinary way being, essentially, let _ = foo a at file scope).

What is more important is function semantics, which should be obvious from the function name. If the name isn't enough, then the type and possibly the documentation provide additional information about the semantics. And once you know the semantics, you can't get the argument count or order wrong.

Quote:
Something I've wondered, is there a concensus on when partially applied functions should be used? I tend too avoid using them to much since, to me at least, they tend to reduce readability (although they can be very useful in some situations). Is this idiomatic Ocaml?


It's idiomatic to use partially applied functions as arguments to other functions, or as local variables to increase readability. Some people also η-reduce functions to make code shorter, but it's not common except on extremely small and straightforward functions.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!