I would like to represent two types of integers in my OCaml program and have the compiler emit an error when one type is used in place of the other. My program converts between the two integer types, and a large fraction of the run time is spent operating on such values. If possible, I would like arithmetic operations to run on unboxed values. I implemented a module that defines such a type, and that implements the +, -, /, * operators. But my understanding is that the operations run on boxed values. Is there a way of getting the same behavior with unboxed values?
module SkipInts = struct
type t = Int of int | SkipInt of int
end
You could use private type abbreviations to create a distinct type for SkipInt represented as an int:
module SkipInt : sig
type t = private int
val of_int : int -> t
val to_int : t -> int
end = struct
type t = int
let of_int x = x
let to_int x = x
end
let _: SkipInt.t = SkipInt.of_int 42
let _: int = (SkipInt.of_int 42 :> int) (* :> can be used instead of SkipInt.to_int *)
let _: SkipInt.t = (42 :> SkipInt.t) (* Error: Type int is not a subtype of SkipInt.t *)
It sounds like you might want something like
module Make_int(): sig
type t [##immediate]
val of_int: int -> t
val to_int: t -> int
type op := t -> t -> int
val ( + ): op
val ( * ) : op
val ( - ): op
val ( / ): op
val ( ~- ): t -> t
end = struct
type t = int
let to_int x = x
let of_int x = x
let ( + ) = ( + ) let ( - ) = ( - ) let ( * ) = ( * ) let ( / ) = ( / )
let ( ~- ) = ( ~- )
end
then any call to Make_int will create a new unboxed int type incompatible with any other type
module Int_1 = Make_int()
module Int_2 = Make_int ()
let fine = Int_1.( of_int 0 + of_int 2 )
let error = Int_1.( of_int 0 + 2 )
let also_error = Int_1.(of_int 0 + Int_2.of_int 1)
Related
I am trying to create a list of lists meaning my result should be like
thatgraph = [[0,[1,2]],[1,[1,3],[1,4],[1,5]],[2,[3,4],[4,4],[5,5],[5,10]]
The code i use is :
fun pin2graph (pin,thatgraph,i,j,rows,cols,havepizza) =
if (i<rows) andalso (j<cols-1) then
(
pin2graph (pin,thatgraph::((i*cols+j),(getAdjacent (pin,i,j,rows,cols,havepizza))),i,j+1,rows,cols,havepizza)
)
else if (i<rows) then
(
pin2graph (pin,thatgraph::((i*cols+j),(getAdjacent (pin,i,j,rows,cols,havepizza))),i+1,0,rows,cols,havepizza)
)
else thatgraph
The getadjacent function is of type :
val getAdjacent = fn
: char Array2.array * int * int * int * int * 'a -> (int * 'a) list
the 'a is actually a real number so the real type is (int * real) list .
Whatever i have tried i always get an operand and operator mismatch with most common being :
operator domain: _ * _ list
operand: _ * (int * (int * 'Z) list)
Any hint would be greatly appreciated , running out of ideas
I want to build a module I with memoization. The type I.t contains a real complex content c, and some mutable properties (eg, mutable is_cool). The module provides outside with functions to calculate and get the properties (eg, is_cool), which can be costly, that is why mutable properties are used:
(*in i.ml *)
module Content = struct
type t = { i: int; mutable j: int }
let is_cool (x: t) : bool = ...
end
module I : sig
type t
val get_c: t -> Content.t
val is_cool: t -> bool
...
end = struct
type t = {
c : Content.t;
mutable is_cool : bool option;
mutable property_a : int option }
let get_c (x: t) -> Content.t = x.c
let is_cool (x: t) : bool =
match x.is_cool with
| Some r -> r
| None -> (* not yet calculated *)
let r = Content.is_cool x.c in
x.is_cool <- Some r;
r
end
...
A concern that I have, is how to code the module I and the code outside such that for any value of type I.t throughout the program execution, its mutable properties are always consistent with its content c. Being consistent means "the mutable properties should either be None or be Some v where v represents the current property of the content".
For instance, the following code tempting to modify directly a property is well forbidden by the signature of the module I.t:
(* in main.ml *)
open I
let test (x: I.t) =
x.is_cool <- Some false
However, it seems that it is not easy to forbid this code, which changes the content:
(* in main.ml *)
let test (x: I.t) (i_new: int) =
let c = I.get_c x in
let c_new = { c with Content.i = i_new } in
let y = { x with c = c_new } in
(* now the content and the properties in y are likely to be inconsistent *)
One way to improve this is to add set in the module I, and always use set x c_new at the place of { x with c = c_new }:
(*in i.ml *)
let set (x: t) (c: Content.t) : t =
{ c = c; is_cool = None; property_a = None }
However, there are still problems, for instance,
1) it is still impossible to forbid people from writing { x with c = c_new }
2) a modification of the mutable components in Content.t (eg, mutable j: int) can also make I.t inconsistent:
(* in main.ml *)
let test (x: I.t) (j_new: int) =
let c = I.get_c x in
c.Content.j <- j_new;
(* now the content and the properties in x are likely to be inconsistent *)
Does anyone know any existing reflexion or solution face to this inconsistency caused by memoization? If we always use set at the place of "record with", and do not allow mutable components in Content.t, can we guarantee 100% the consistency for any code scenario?
Well, you could use private
Your code would look like this :
module type C = sig
type t = private { i: int; mutable j: int }
val new_c : int -> int -> t
val is_cool : t -> bool
end
module Content : C = struct
type t = { i: int; mutable j: int }
let new_c = ...
let is_cool (x: t) : bool = ...
end
module I : sig
type t
val new_i : int -> int -> t
val get_c: t -> Content.t
val is_cool: t -> bool
...
end = struct
type t = {
c : Content.t;
mutable is_cool : bool option;
mutable property_a : int option }
let new_i = ...
let get_c (x: t) -> Content.t = x.c
let is_cool (x: t) : bool =
match x.is_cool with
| Some r -> r
| None -> (* not yet calculated *)
let r = Content.is_cool x.c in
x.is_cool <- Some r;
r
end
...
And if you try to write :
let c = {i = 3; j = 3};;
it will reject you answering :
Error: Cannot create values of the private type Content.t
When I only have datatype Nt = int | string, sml doesn't complain. But when I also have val n = 6 : Nt, ml doesn't accept 6 as an Nt. Why is this? I do know that, normally there should be data constructers before int and string, but here I'm having that to define functions that can take either int or string.
You are misinterpreting the code. To be clear, you cannot define datatypes without constructors. But ML has different name spaces for types and values. The occurrences of int and string in your example are value identifiers. As such, they just define new nullary constructors, and have absolutely zero to do with the types of the same name. You can now define val n = int : Nt. It is as if you had written datatype Nt = foo | bar.
Having a function that can either take an int or a string can be interpreted in two ways. You could mean that you want a function that could take anything and do something general with it – that would be a polymorphic function. E.g.
fun id x = x
can take both ints and strings and return them, but not do much with their content in specific. If you want a function that can take either an int or a string and do something different with them, depending on which input you have, you could use a union type, e.g.
datatype Nt = Int of int (* constructor has the type int -> Nt *)
| Str of string (* constructor has the type string -> Nt *)
val sample_1 = Int 42
val sample_2 = Str "Hello"
Here, Int and Str are value constructors that work like functions in that they take a value of type int/string, respectively, as argument and return a value of the union type Nt. I've named them something other than int and string to signify that the value constructors are different from the types int and string. If they did not take an argument, their only use would be to distinguish one from the other (in which case they would be isomorphic to true/false).
A function that takes such a value as input would have to match against the pattern constructors of the same names. Here are some functions that would take this union type as argument:
fun isAnInt (Int i) = true
| isAnInt (Str s) = false
fun intVal (Int i) = i
| intVal (Str i) = 0
fun strVal (Int i) = Int.toString i
| strVal (Str s) = s
fun sumNt [] = 0
| sumNt (x::xs) = intVal x + sumNt xs
fun concatNt [] = ""
| concatNt (x::xs) = strVal x ^ concatNt xs
And here these functions are being tested:
val test_isAnInt_1 = isAnInt sample_1 = true
val test_isAnInt_2 = isAnInt sample_2 = false
val test_intVal_1 = intVal sample_1 = 42
val test_intVal_2 = intVal sample_2 = 0
val test_strVal_1 = strVal sample_1 = "42"
val test_strVal_2 = strVal sample_2 = "Hello"
val test_sumNt_1 = sumNt [] = 0
val test_sumNt_2 = sumNt [sample_1, sample_1, sample_2, sample_1] = 126
val test_sumNt_3 = sumNt [sample_2, sample_2, sample_2] = 0
val test_concatNt_1 = concatNt [] = ""
val test_concatNt_2 = concatNt [sample_1, sample_1, sample_1] = "424242"
val test_concatNt_3 = concatNt [sample_1, sample_2, sample_1] = "42Hello42"
This function should be able to simplify, removing the unnecessary let module T = .... But how?! I keep getting syntax errors. Tips?
let make_module dice_results : (module Tellstory.T) =
let dice_calls = ref 0 in (* nr of times dice has been called *)
let module T = Tellstory.Make(struct
let dice n =
let result = List.nth dice_results (!dice_calls) in
dice_calls := !dice_calls + 1;
result
end) in
(module T)
I can't check your module, but this is an example for inspiration:
let make_module () : (module Unit) =
(module Comparable.Make (struct type t = int with compare, sexp end))
Looks like that the following, should work:
let make_module dice_results : (module Tellstory.T) =
let dice_calls = ref 0 in (* nr of times dice has been called *)
(module Tellstory.Make(struct
let dice n =
let result = List.nth dice_results (!dice_calls) in
dice_calls := !dice_calls + 1;
result
end))
I'm using the Bitstring module in the following code:
let build_data_32 v wid =
let num = wid / 32 in
let v' = Int32.of_int(v) in
let rec aux lst vv w = match w with
0 -> lst
| _ -> (BITSTRING { vv : 32 } ) :: ( aux lst (Int32.succ vv) (w-1)) in
Bitstring.concat ( aux [] v' num ) ;;
Note that when you have BITSTRING { vv : 32 }
that vv is expected to be an Int32 value. I'd like to generalize this function to work with different widths of bitstrings; ie, I'd like to create a build_data_n function where the bitstring would be constructied with BITSTRING { vv : n } .
However, the problem here is that if n is less than 32 then the succ function used above would just be the succ for type int. If it's greater than 32 it would be Int64.succ Same issue above in the line let v' = Int32.of_int(v) in - for values less than 32 it would simply be: let v' = v in , whereas for values greater than 32 it would be: let v' = Int64.of_int(v) in
Is this a case where a functor would come in handy to generalize this function and if so, how would I set that up? (and if there's some other way to do this that doesn't require functors, that would be nice to know as well)
There are a few approaches available. One is to use a functor, similar to the following:
(* The signature a module needs to match for use below *)
module type S = sig
type t
val succ : t -> t
val of_int : int -> t
end
(* The functor *)
module Make(M : S) = struct
(* You could "open M" here if you wanted to save some typing *)
let build_data v =
M.succ (M.of_int v)
end
(* Making modules with the functor *)
module Implementation32 = Make(Int32)
module Implementation64 = Make(Int64)
let foo32 = Implementation32.build_data 12
let foo64 = Implementation64.build_data 12
Another is to wrap your data type in a record:
(* A record to hold the relevant functions *)
type 'a wrapper_t = { x : 'a; succ : 'a -> 'a }
(* Use values of type 'a wrapper_t in *)
let build_data v =
v.succ v.x
(* Helper function to create 'a wrapper_t values *)
let make_int32_wrapper x = { x = Int32.of_int x; succ = Int32.succ }
let make_int64_wrapper x = { x = Int64.of_int x; succ = Int64.succ }
(* Do something with a wrapped int32 *)
let foo32 = build_data (make_int32_wrapper 12)
let foo64 = build_data (make_int64_wrapper 12)
And finally, if you are using OCaml 3.12.0 or later, you can use first class modules:
(* You can use the module type S from the first example here *)
let build_data (type s) m x =
let module M = (val m : S with type t = s) in
M.succ x
let int32_s = (module Int32 : S with type t = Int32.t)
let int64_s = (module Int64 : S with type t = Int64.t)
let foo32 = build_data int32_s 12l
let foo64 = build_data int64_s 12L
Each of these approaches can be mixed and matched. You may also be able to wrap your values in variant types or objects to get a similar result.
With BITSTRING { vv : n }, i.e. using runtime-specified field length, the type of vv cannot depend on n as it is not the compile-time constant anymore, so vv is forced to int64.