I am new to Ocaml. Here is one question in my class:
let f a b c d e = e.( if e.(1) then a b else b d )
We were asked to inferred the type of "a". I think it is an int, because this if operation should return an int to be the index of e array. But my answer is wrong. Can anyone help me to analyze this ? Thank you very much !
The value of the if is either a b or b d. Since the expression a b must return an int (as you say), a must be a function that accepts one argument and returns an int. So a is not an int but a function.
The type of the parameter of a is also part of the type of a. To determine this type, note that b is also a function that returns an int (as you can tell from the subexpression b d). So a is a function that accepts one argument that is a function and returns int. Since there is nothing in the code to limit the type of d, b can be any function that accepts one argument and returns an int. Let's call the type of b's argument 't. Note that this type can be different for different calls to f. So then the type of a is ('t -> int) -> int, where the corresponding type of b is 't -> int and the type of d is 't.
This is all pretty complicated. Possibly there's a typo in your transcription. Or maybe it's good to work out a complicated example :-)
the above statement is equivalent to:
fun f(a,b,c,d) = e[ if e[1] then a(b) else b(d) ]
__
Note:
"if e[1] then a(b) else b(d)" : this should be an int, as it act as the index of e array. function will return the same type, so Type of a(b) and b(d) are equal and should return integer.
e[1] = this should be boolean, that means e is an array of boolean.
Hence what we concluded
e : array of boolean
a : function returns an integer
b : function returns an integer.
we can't further type inference for c and d.
Related
module type ORDER = sig
type t
val leq : t -> t -> bool
val equal : t -> t -> bool
end
module Int:ORDER with type t = int = struct
type t = int
let leq = (<=)
let equal = (=)
end
Can someone explain to me this line :
module Int:ORDER with type t = int = struct
this --> with type t = int
I've tried without it :
Int.equal 3 3
Line 1, characters 10-11:
Error: This expression has type int but an expression was expected of type
Int.t
I can see what "It does", but I'm unable to explain it in words what it is happening, thank you
The colon operator in a module expression isn't just a signature constraint, it's also an abstraction construct. M : S requires the module M to have the signature S, and tells the compiler to forget everything about typing of M except for what is specified in S. This is where abstraction is born.
Given the definition module Int: S = struct … end (which is syntactic sugar for module S = (struct … end : S)), all the compiler knows about the types of elements of Int is what is recorded in S. If S is ORDER, its type t is abstract, and therefore Int.t is an abstract type: the fac that Int.t is actually an alias for int is hidden. Hiding the real implementation of a type is exactly what abstract types are about.
The signature that is actually desired for Int is
sig
type t = int
val leq : t -> t -> bool
val equal : t -> t -> bool
end
This is almost exactly the signature called ORDER, but with the type t being an alias for int rather than abstract. The with type construct allows using the name ORDER to construct the module type expression above. Given the definition of ORDER, writing
module Int : ORDER with type t = int = struct … end
is equivalent to writing
module Int : sig
type t = int
val leq : t -> t -> bool
val equal : t -> t -> bool
end = struct … end
Since the type Int.t is transparently equal to int, it can be used interchangeably with int.
A ORDER signature is mostly useful to pass the type to functors like Set and Map that build data structures that rely on an ordering relation for their elements. The data structure only depends on the abstract properties of the order relation, but the code that uses the data structure can still be aware of the type of the elements (thanks to another with type constraint, this one equating the type of data structure elements with the type of the functor argument). See “functors and type abstraction” in the language introduction.
I'm just starting to learn OCaml, and I was confused by how the OCaml compiler determines the input type of arguments in certain situations where the argument could be multiple types. I'm assuming I would need to explicitly state the type in these instances? For example:
let sign x =
if x > 0 then 1
else if x < 0 then -1
else 0
let _ = print_int(sign 1.5)
Throws "Error: This expression has type float but an expression was expected of type int"
But don't the comparison operators work with floats as well? Why does the compiler assume that the argument should be an int instead of saying something like the argument type is ambiguous (unless I'm mistaken and the type actually is not ambiguous)?
The built-in comparison operators in OCaml have the following type signature:
'a -> 'a -> bool. Note that while the argument types are generic ('a), they are both the same. So, when you have a comparison like x < 0, the compiler sees that the type of the second argument is int and then infer that the type of x must be int as well. Changing your comparisons to use floats, e.g. x < 0. will make your code type-check, but then it would no longer work for int inputs.
The Rust Book section on the as operator currently says
The as keyword does basic casting:
let x: i32 = 5;
let y = x as i64;
It only allows certain kinds of casting, however.
What are those certain kinds of allowed casting?
A since-deleted answer here explained that sometimes you need to chain multiple as-casts to accomplish a safe result, that can't be done in a single step. When is that necessary?
I don't think that this is documented very well, but here is some information you might find useful:
A cast e as U is valid if one of the following holds:
e has type T and T coerces to U; coercion-cast
e has type *T, U is *U_0, and either U_0: Sized or
unsize_kind(T) = unsize_kind(U_0); ptr-ptr-cast
e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
e has type T and T and U are any numeric types; numeric-cast
e is a C-like enum and U is an integer type; enum-cast
e has type bool or char and U is an integer; prim-int-cast
e has type u8 and U is char; u8-char-cast
e has type &[T; n] and U is *const T; array-ptr-cast
e is a function pointer type and U has type *T,
while T: Sized; fptr-ptr-cast
e is a function pointer type and U is an integer; fptr-addr-cast
where &.T and *T are references of either mutability,
and where unsize_kind(T) is the kind of the unsize info
in T - the vtable for a trait definition (e.g. fmt::Display or
Iterator, not Iterator<Item=u8>) or a length (or () if T: Sized).
Note that lengths are not adjusted when casting raw slices -
T: *const [u16] as *const [u8] creates a slice that only includes
half of the original memory.
Casting is not transitive, that is, even if e as U1 as U2 is a valid
expression, e as U2 is not necessarily so (in fact it will only be valid if
U1 coerces to U2).
Quoted from The Rustonomicon: Casts
Here's an exhaustive list of all the true casts. For brevity, we will use * to denote either a *const or *mut, and integer to denote any integral primitive:
*T as *U where T, U: Sized
*T as *U TODO: explain unsized situation
*T as integer
integer as *T
number as number
C-like-enum as integer
bool as integer
char as integer
u8 as char
&[T; n] as *const T
fn as *T where T: Sized
fn as integer
I started learning functional programming (OCaml), but I don't understand one important topic about functional programming: types.
Can anyone explain me this solution please?
Have a test this week and can't get reach the resolution..
let f a b c = a (a b c) 0;;
f: ('a -> int -> 'a) -> 'a -> int -> 'a
let f a b c = a (a b c) 0;;
Your confusion involves types and type inference, i.e., when you define a function or binding, you don't need to give explicit types for its parameters, nor the function/binding itself, OCaml will figure it out if your definition of function/binding is correct.
So, let's do some manual inferences ourselves. If a human can do, then the compiler can also do.
1.
let x = 1
1 is integer, so x must be an integer. So you don't need to do int x = 1 as in other languages, right?
2.
let f x = 1
If there are multiple variable names between let and =, then it must be a function definition, right? Otherwise, it won't make sense. In Java like language, it also makes no sense to say int x y = 1, right?
So f is a function and x is must be a parameter. Since the righthand side of = is an integer, then we know f will return an integer. For x, we don't know, so x will be thought as a polymorphic type 'a.
So f: 'a -> int = <fun>.
3.
let f a b c = a (a b c) 0;;
f is a function, with parameters a, b, c.
a must be a function, because on the righthand side of =, it is a function application.
a takes two arguments: (a b c) and 0. 0 is an integer, so the 2nd parameter of a must be an integer type.
Look inside (a b c), c is the 2nd arguement, so c must be integer.
We can't infer on b. So b is 'a.
Since (a b c) can be the 1st argument of a, and (a b c) itself is an application on function a, the return type of a will have the same type of b which is 'a.
Combine information above together, you get f: ('a -> int -> 'a) -> 'a -> int -> 'a.
If you want to learn it formally, https://realworldocaml.org/ is your friend.
Let's say, in C++11, I do
auto a = 4;
What will a be? An int (as I often read), an unsigned int, a short, a long, a size_t, a char? Is the behaviour of auto always defined, will it always be the exact same type (with the exact same bit length!) on each compiler and each architecture?
Another example:
class A{};
class B:A{};
auto x = new B();
Will x be of type *B or of type *A? Always the same on each compiler and platform? Both are perfectly legit, how does the compiler know which one I intend?
Is there an exact list of the auto behaviour?
What will a be?
int, since that's the type of 4.
Will x be of type *B or of type *A?
B*, since that's the type of new B().
Is there an exact list of the auto behaviour?
Usually, it's the type of the initialiser; unless that's a reference type, in which case it's the underlying object type. There are a few other wrinkles for unusual types like arrays, as mentioned in the comments.
will it always be the exact same type (with the exact same bit length!) on each compiler and each architecture?
In most cases, the initialiser has a well-defined type, and that determines the type deduced by auto.
If the initialiser is an integer literal, then the type might depend on the platform; for example, 1000000 might be int on a 32-bit platform, but long on a 16-bit platform.
Every expression in C++ has a type. auto can only be used
when there is an initialization expression. The type will be
the type of that expression. The expression 4, for example,
has type int, always, and the type of new B() is B*,
always.
Of course, the fact that the type is clear to the compiler
doesn't mean that it is clear to the reader. Abuse of auto is
a good way of rendering a program unreadable, and also of making
it fragile, since the compiler cannot check whether they type of
the initialization expression is compatible with the desired
type.
In the first case, a will be a int, see bellow:
auto a = 4 ; // int
auto b = 4U ; // unsigned int
auto c = 4L ; // long int
auto d = 4LLU ; // unsigned long long int, maybe it's ULL i don't remember...
auto x = 4.0 ; // double
auto y = 4.0f ; // float
In fact, there is a way to write any 'type of' int in C.
For new B(), well the compiler will take the only answer, which is B *.
auto match the type of the right value the variable is assigned to, without trying to infer anything, it's not its job!
You should not see the auto keyword as a magic stuff, but just something than may help you in case you don't want to have big declaration type.
The compiler doesn't know what you intend, and it doesn't care. The type of a or x is the type of the expression on the right hand side. Since the type of 4 is int, and the type of new B() is B*, it's the same as if you wrote int a = 4; B* x = new B();