I’m writing an interactive calculator in OCaml with some simple commands. Users should be able, among other things, to define their own simple functions (mathematical functions), for instance
let f(x) = x
let g(x) = 2*f(x)
Now, the functions should be handled like in functional languages, that means they should remember their time-of-creation environment. That means, that with a function I have to keep a closure of its environment, which is functions and variables.
I keep currently defined functions in a list of tuples formed like (functions_present_at_the_time_of_creation, variables_present_at_the_time_of_creation, function_name, function_argument_names, function_formula). When I try to add a new function to the list of functions (let’s assume, that it’s not currently defined and I don’t have to overwrite anything), I recurrently iterate to the end of the list of functions and there would like to add a new tuple.
The problem is, assuming my current functions list is of type (a*b*c*d*e) list when i try to add a tuple with itself to the end of it, it changes its type to ((a*b*c*d*e) list*f*g*h*i) list. What can I do to be able to perform such addition of a list to itself, encapsulated in a tuple?
Here’s some simple SSCCE I wrote while trying to find a workaround to this issue.
let rec add_to_end list list_copy dummy = match list with
| [] -> [(list_copy, dummy)]
| h::t -> h::(add_to_end t list_copy dummy)
let add list dummy = add_to_end list list dummy
This one tries to do it with a copy of the list. The following one is written without using of a copy (both of these examples don’t work, of course):
let rec add_to_end list dummy = match list with
| [] -> [(list, dummy)]
| h::t -> h::(add_to_end t dummy)
The first example doesn’t work when trying to use the function add, but when doing it for instance this way (in the interpreter):
let l = [];;
let l = add_to_end l l 1;;
let l = add_to_end l l 2;;
let l = add_to_end l l 3;;
Then it works fine. I’d appreciate any help, I may think about changing the design also, any proposals are very welcome.
Edit: Here’s the output of the above commands:
# let l = [];;
val l : 'a list = []
# let l = add_to_end l l 1;;
val l : ('a list * int) list = [([], 1)]
# let l = add_to_end l l 2;;
val l : (('a list * int) list * int) list = [([], 1); ([([], 1)], 2)]
# let l = add_to_end l l 3;;
val l : ((('a list * int) list * int) list * int) list =
[([], 1); ([([], 1)], 2); ([([], 1); ([([], 1)], 2)], 3)]
It’s hard to tell whether you’re aware that OCaml lists are immutable. You can’t add a value to the end of an existing list. An existing list can never be changed. You can create a new list with a value added to the end. If you do this, I don’t see why you would want to add a pair to the end consisting of the list and the new value. I suspect you’re thinking about it wrong. Here’s a function that takes a list and an integer and adds the integer to the end of the list.
The function returns a new list with the value added to the end. The original list isn’t changed.
As a side comment, it’s much more idiomatic to add values to the front of a list. Repeatedly adding to the end of the list is slow–it gives quadratic behavior. If you want the other order, the usual thing to do is add everything to the front and then reverse the list–this is still linear.
Edit
Apparently you really want a function that looks something like this:
let f a list = list @ [(list, a)]
This is not realistically possible, the types don’t work out right. A list can contain things of only one type. So you can conclude that the type of the list
tis the same as the type(t, v) list, wherevis the type of a. This is a recursive type, not something you would really want to be working with (IMHO).You can actually get this type in OCaml using
-rectypes:But (as I say) it’s something I would avoid.
Edit 2
Now that I look at it, your first code sample avoids requiring a recursive type because you
specify two different copies of the list. Until you call the function with the same list, these are potentially different types. So the function type is not recursive. When you call with two copies of the same list, you create a new value with a type that’s different than the type of the list. It only works because you’re using the same name
lfor different values (with different types). It won’t work in a real program, where you’d need a single type representing your list.As another side comment: the beauty of adding values to the beginning of a list is that the old value of the list is still there. It’s the tail of the new list. This seems lot closer to what you might actually want to do.