Confusing type declaration? - sml

I haven't worked with SML in awhile and I came across this line of code:
type memory = string -> int;
Does this define 'memory' to be a function which takes a string a returns an int, or something else entirely? I've searched for a similar declaration but I can't seem to find one or figure out what it does.
When I put it into SML/NJ, I just get this:
- type memory = string -> int;
type memory = string -> int

memory is not a function , it's just an abbreviation for a type that is a function that takes as input a string and returns an int.
So whenever you would like to write that something is of type string->int you can just write it's of type memory.
For example instead of writing:
- fun foo(f : string->int, s) = f s;
val foo = fn : (string -> int) * string -> int
you could write:
- fun foo( f: memory, s) = f s;
val foo = fn : memory * string -> int
Such type declarations can make your code more readable (e.g. instead of writing that a pair x is of type int*int like (x: int*int), you can just create an abbreviation type pair = int*int and then you can write that x is of type pair like this (x: pair)).

Related

I realized this is wrong but I could not delete it

Author: This of course can not be done. Learn from the answers below.
In C++ how do we do the following
// fundamental language construct
type name = value ;
// for example
int x = y;
with function pointers?
typedef (char)(*FP)(unsigned);
// AFAIK not possible in C++
FP x = y ;
I can use lambdas:
FP x = []( unsigned k) -> char { return char(k); }
But I do not know how to do this without the lambda. Any ideas?
Whenever you can write a typedef, you can also write a variable declaration with no typedef, with almost identical syntax.
Example:
// typedef
typedef char(*FP)(unsigned);
FP x = y ;
// no typedef
char(*x)(unsigned) = y;
Remove the typedef keyword, and you have a variable declaration. Slap an initialisation on it if you want.
You can use auto:
auto fptr = &f;
It skips the need of a typedef and conserve a nice syntax.
It is almost the same as Lambdas, but hard to read i think:
void my_int_func(int x)
{
std::cout << "ther param is: " << x << std::endl;
}
//
int main(int argc, char *argv[])
{
void (*foo)(int) = my_int_func;
foo(1);
But I do not know how to do this without lambda. Any ideas?
Just dont use a lambda but a function:
typedef char(*FP)(unsigned);
char foo(unsigned){ return 0;}
int main() {
FP x = foo;
}
Function pointer typedefs are rather nasty, if you can better use using:
using FP = char(*)(unsigned);
Live Demo
Well... if you're using lambdas, you can also use auto, so
auto x = foo;
The following is a full compiling example with a static_assert() that verify the obtained type
#include <type_traits>
char foo (unsigned)
{ return ' '; }
int main ()
{
auto x = foo;
static_assert( std::is_same<decltype(x), char(*)(unsigned)>::value, "!" );
}
Using auto with lambda in the way you used it with FP
auto y = []() ->bool { return true; };
leads to something different: the type of y above is an unnamed class with an operator(), not a function pointer type to that operator().
If you want a pointer to function, you have to convert the lambda to it using the operator +, as you can verify with the following static_assert()
auto y = +[]() ->bool { return true; };
static_assert( std::is_same<decltype(y), bool(*)()>::value, "!" );
Many thanks all for the lively roller-coaster of useful comments. Somebody on Reddit, where I asked the same question, under the user name "TheTiefMaster", dropped this "one liner":
// also works as C
char whatever(unsigned k) { return char(k); } char(*F)(unsigned) = whatever;
Let me clarify: I do understand these are two statements on one line. And no there is no type in here, but one function pointer pointing to the same function. The usage:
auto x = whatever(65); // 'A'
auto y = F(66); // 'B'
Then I figured the following will make the function definition and its type declaration:
// FP is a type of function whoever
char whoever(unsigned k) { return 'A'; } typedef char(*FP)(unsigned) ;
Calling whoever behaves as expected
auto w = whoever(42) ; // 'A'
FP is where it starts getting interesting. FP is a type, and as it turns out one can cast to the type.
// using FP as a type
// c++ style type cast
// empty cast returns nullptr
auto fun = FP();
// calling fun() above crashes
// but it is "invocable" as per C++ rules
static_assert(std::is_invocable_v<P2F()>);
Passing any argument to this cast, works and returns non null address:
// update: this compiles only on MSVC
// and is a bug
auto fun = FP(42);
// type of fun is char (*) (unsigned)
Calling the result of this fun crashes, obviously:
// reading access violation
fun(123) ;
This cast with an instance from any required function, works:
auto fun = FP(whatever);
// works, obviously
fun(65) ; // 'A'
To use this knowledge we will use the static_cast to safely cast to what we can call. C++ type cast is too forceful, just like C style type cast is.
// does not compile
// fun is the wrong type and can not be called
auto fun = static_cast<FP>(42);
// does compile, fun is safe to call
auto fun = static_cast<FP>(whatever);
// works, obviously
fun(65) ; // 'A'
This investigation is obviously far from over. I shall proceed with it, elsewhere.
Update:
using FP = char (*)(int) ;
// must not compile, compiles under MSVC
auto oops = FP(42) ;
Is the bug in MSVC, I reported it today.
The code:
typedef char(*FP)(int);
FP x = y;
fails to compile with current C++ compilers if y is a lambda expression capturing a variable.
// Compiles OK
FP x0 = [](int k) -> char { return char(k); };
// Fails to compile
int i = 123;
FP x1 = [=](int k) -> char { return char(k); };
FP x2 = [=](int k) -> char { return char(k+i); };
FP x3 = [&](int k) -> char { return char(k+i); };
FP x4 = [i](int k) -> char { return char(k+i); };
// error: cannot convert ‘main()::<lambda(int)>’ to ‘FP {aka char (*)(int)}’
// in initialization
The reason why it fails to compile is that the size of the right side of the assignment to x1...x4 is greater than size of FP.
For a C++ compiler to make assignments to x1...x4 be valid it would need to generate code at runtime. Current C++ compilers such as GCC and clang do not support this, mainly because it would cause memory leaks because C++ isn't a garbage collected language. Some garbage collected language implementations, such as earlier versions of the official Go compiler, do support such assignments by performing runtime code generation.

How to fix the OCaml compile error when using type annotations with functors?

I am new to OCaml and I am trying use functors. When I use the module type annotations with the functors, this results in a compile time error in my code.
When I remove the : Printable (from the module FromToString line) and the : ToString (from the module IntToString line) annotations, the following program compiles without error:
module type ToString =
sig
type t
val to_string: t -> string
end
module type Printable =
sig
type t
val print: t -> unit
end
module FromToString (S:ToString) : Printable =
struct
type t = S.t
let print a = print_string ( S.to_string a)
end
module IntToString : ToString =
struct
type t = int
let to_string = string_of_int
end
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3
However, when I add these annotations (as shown in the code) the compiler gives the following error:
File "Functor.ml", line 26, characters 28-29:
Error: This expression has type int but an expression was expected of type
PrintableInt.t = FromToString(IntToString).t
How can I use these annotations without causing a compile error?
The root issue is that your signature constraints make the resulting module far too opaque. When you constrain your functor result:
module FromToString (S:ToString) : Printable = ...
you are making the type t an abstract type, which can only be consumed by the to_string function and never been produced. In other words, a module of type Printable is unusable by itself.
When starting with functor, it is very often useful to look at the module type inferred by the compiler for the resulting module.
In the FromToString case, this is:
module FromToString (S:ToString) : sig
type t = S.t
val print: t -> unit
end = ...
You can see that the inferred module type of the result
sig
type t = S.t
val print: t -> unit
end
that it is quite similar to Printable except that now the type t is equal to the type t of the argument module S.
Thus, it is possible to reuse Printable to write the full module type of the result by adding a type equality with a with constraint:
module FromToString (S:ToString): Printable with type t = S.t = struct
type t = S.t
let print a = print_string ( S.to_string a)
end
The same issue appears for IntToString and can be fixed in a similar way:
module IntToString : ToString with type t = int =
struct
type t = int
let to_string = string_of_int
end
Then the compiler error is gone:
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3
One has to make the type t public, using the with notation:
module type ToString =
sig
type t
val to_string: t -> string
end
module type Printable =
sig
type t
val print: t -> unit
end
module FromToString (S:ToString) : Printable with type t = S.t =
struct
type t = S.t
let print a = print_string ( S.to_string a)
end
module IntToString : ToString with type t =int =
struct
type t = int
let to_string = string_of_int
end
module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3

Losing type precision from module signature

Let's say I had a simple module MyFoo that looks something like this
module MyFoo = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
With this definition, it works great and as expected — I can do something like
let _ = MyFoo.A "";;
- : MyFoo.t = MyFoo.A ""
without any problems.
Now maybe I want to create a functor that consumes modules with this structure, so I define a module signature that describes generally what this looks like and call it BaseFoo
module type BaseFoo = sig
type t
val to_string : t -> string
end
If I redefine MyFoo the same way but giving it this signature like
module MyFoo : BaseFoo = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
I lose the precision of its type t (is there a better way to describe what happens here?) — for example:
let _ = MyFoo.A "";;
Error: Unbound constructor MyFoo.A
What exactly is going on here and why does it happen? Is there a canonical way for dealing with this kind of problem (besides just leaving off the signature)?
I've tried manually including the signature and the specific type type definition too but get a different kind of error (this probably isn't the right approach).
module MyFoo : sig
include BaseFoo
type t = | A of string | B of int
end = struct
type t =
| A of string
| B of int
let to_string thing =
match thing with
| A str -> str
| B n -> string_of_int n
end
let _ = MyFoo.A "test";;
Error: Multiple definition of the type name t.
Names must be unique in a given structure or signature.
You don't need the signature
What is going on is pretty much what you describe: giving MyFoo the BaseFoo signature in its definition restricts it to the signature.
Why? Because this is what specifying a signature at this place is for. The canonical solution is to leave of the signature (usually, letting the signature definition next to the module definition will be clear enough for the reader).
Note that when you call MyFoo on your functor, the signature will be checked. My usual choice is to rely on that.
A few workarounds
Given what you've tried, I guess this could be interesting to you:
module type BaseFoo = sig ... end
module MyFoo = struct ... end
module MyFooHidden : BaseFoo = MyFoo (* Same as defining MyFoo : BaseFoo *)
module MyFooWithType :
BaseFoo with type t = MyFoo.t
= MyFoo (* What you want *)
The with type t = t' clause allows you to annotate a module signature to add type information to it. It is quite useful, especially when dealing with functors. See here for more information.
MyFooHidden may seem useless, but you can see it as a check that MyFoo has the right signature. You can still use MyFoo however you want after all. MyFooWithType is actually (a bit) less useful because if you change your signature to add a type you'd want exported, you'd need to add the export here too.
Using include
As for your include try. Well, nice try! You were almost there:
module MyFoo : sig
type t = A of string | B of int
include BaseFoo with type t := t
end
The with type t := t' is a bit different in that it doesn't perform an equality but a replacement. The type t definition is removed from the BaseFoo signature altogether and all instances are replaced with your own t, that way you don't get any double definition problem. See here for more details.
As you point out, this is probably not the approach you want, as you no longer easily know that MyFoo is indeed a BaseFoo.

What does (int -> int) mean?

I'm trying to decipher the meaning of the type choice part of this. From what I can gather, it's multiplying an int, by (int -> int), by another int. I'm just confused as to what (int -> int) means. Any help would be much appreciated.
val callcc = SMLofNJ.Cont.callcc
val throw = SMLofNJ.Cont.throw
type 'a cont = 'a SMLofNJ.Cont.cont
type choice = int cont * (int -> int) * int
( int -> int ) means a function that takes 1 int argument and returns an int. What you are defining is not a multiplication. 'choice' is a type defined as a 3-tuple. The 3-tuple consist of an int, a function that takes an int and returns an int, and finally another int.
Let me start by clarifying some things that I think you've misunderstood:
type choice = ... is declaring choice as a "type constructor", meaning that it denotes a type rather than a value. The right-hand-side is then a "type expression"; for example, you could write type myInt = int to declare myInt as an alternative name for the int type.
int * int, in a type expression, doesn't mean "an int times an int"; rather, it denotes the type "pair of int-s". For example, the value expression (1, 2) has type int * int.
That out of the way . . . int -> int denotes the type "function that takes an int and returns an int". For example, the value expression fn i => 2 * i, which is a function that takes an integer and returns double that integer, has type int -> int.
Putting this together,
type choice = int cont * (int -> int) * int
declares choice to mean "a triple (3-tuple) whose first element is an int cont; whose second element is a function that takes an int and returns an int; and whose third element is an int".

Musing about variable declaration and the typedef specifier in c++

If you want to declare an int you do for instance
int x;
You could after assign a value to x (or "define" x) for instance as follows :
x = 3;
Of course, you could have done directly
int x = 3;
There are types whose variables should be declared and defined at the same time, for instance
const double y = 2.3;
as well as type whose variables you simply can't declare and define at the same time, for instance a pointer to an array of three char's :
typedef char (*pc3)[3];
char c3[3] = "ok";
pc3 apc3 = &c3;
(Am I wrong on the two previous examples ? Is there a one-liner (only one semi-column allowed) for the last one ?)
Consider now the following function pointer definition :
typedef int (*ipfunci)(int);
ipfunci fptr = &f; // where f is some int -> int function
(no one-liner for this as far as I know). Perfectly legal of course, but what about
typedef int (ifunci)(int);
Perfectly legal as well, but you can't define a variable of "type" 'ifunci' after having declared it, and what's it's use ?
This boils down to my real question : looking at
typedef char (*pc3)[3];
typedef int (*ipfunci)(int);
one sees an analogy between the two definitions, if one decides to see a size 3 array of char's as a function
0 -> char
1 -> char
2 -> char
One could also see
typedef int * pi ;
as the definition of the "type" pi as constant function with value equal to an int.
How far does this synthactic and functional analogy go ? What is behind types whose definitions really require a typedef : are they systematically "functional" types ?