Sometimes one wants to write bindings for some C code using ocaml modules as interface or optimize some method of an object in C. But one can't define the external function before the object because it needs the type of the object and one can't define the object because it needs to call the external function.
One solution is to use recursive modules but that easily tripples the source size and becomes awfully unreadable. The external functions also enter the modules namespace and need to be hidden again. So what is a shorter, more readable solution?
I figured out how to use first class modules to define an external function directly inside an objec. First one needs an module type to be used by the first class module. This is generic and can be used for any kind of external function over and over:
module type External = sig
type e
val stub : e
end
after that once can declare external functions like this:
class c =
let ext = (module struct
type e = c -> unit
external stub : c -> unit = "stub"
end : External with type e = c -> unit)
in
object(self:'self)
method as_c = (self :> c)
method foo =
let module E = (val ext)
in
E.stub self#as_c
end
or even more local within the method itself as:
class c = object(self:'self)
method as_c = (self :> c)
method foo =
let module E = (val (module struct
type t = c -> unit
external stub : c -> unit = "stub"
end) : External with type t = c -> unit)
in
E.stub self#as_c
end
Note: the as_c method avoids the (open) self type getting closed or escaping its scope.
Related
Here's the situation:
In a library:
export fctn( simple int m )
...
In the calling code:
type Test
int n
inst = Test.new( n = 1 )
y = Library.fctn( inst.n )
...
This produces an error message that the function is being called with a "series int" when it must be called with a "simple int".
I may be missing something here, and it may be dealing with the simple input to the function. There were no errors when I avoided the object approach.
Suppose that we have an object which has some properties of type proc:
type
x = object
y: proc(a,b:int)
proc myproc(a,b:int) =
echo a
var tmp = new x
tmp.y = myproc # I want to insert initial value in this line for example a = 1
tmp.y(5)
How can I insert initial values in the specified line, and not anywhere else?
Thank you in advance
As far as I know it's not possible to do what you want without other modifications, because you have specified y to be a proc receiving two parameters. So whatever you assign to it, the compiler is always going to expect you to put two parameters at the call site.
One alternate approach would be to use default values in the proc definition:
type
x = object
y: proc(a: int = 1, b: int)
proc myproc(a,b: int) =
echo(a, " something ", b)
var tmp = new x
tmp.y = myproc
tmp.y(b = 5)
The problems with this solution are of course that you can't change the value of a at runtime, and you are forced to manually specify the name of the parameter, otherwise the compiler is going to presume you are meaning the first and forgot to specify b. Such is the life of a non dynamic language.
Another approach is to define the proc as having a single input parameter, and then using an anonymous proc or lambda to curry whatever values you want:
type
x = object
y: proc(a: int)
proc myproc(a,b: int) =
echo(a, " something ", b)
var tmp = new x
tmp.y = proc (x: int) = myproc(1, x)
tmp.y(5)
If you were to use the sugar module, as suggested in the docs, the assignment line could look like:
tmp.y = (x: int) => myproc(1, x)
I'm trying to better understand reasoning behind the value types.
Does value/reference types in Nim works same way as in Swift?
yes, value and reference types do work in Nim as in Swift and other low-level programming languages like C#, C++, Rust. By that I mean that they follow these semantics with respect to copy:
Value semantics means that the copy owns its memory and lives separately from the copied.
Reference semantics means that the copy and the copied refer to the same underlying memory location.
(taken from this forum answer).
As an example, I translate this swift blog post to nim (playground):
# port of https://developer.apple.com/swift/blog/?id=10 to Nim
## Example of Value type
type S = object
data: int # I cannot say the default of data is -1. Nim does not have (yet) custom initialization, see accepted RFC https://github.com/nim-lang/RFCs/issues/252
var a = S()
var b = a
a.data = 42
echo (a, b) # ((data: 42), (data: 0))
## Example of Reference type
type C = ref object
data: int
func `$`(c: C): string = "C(data: " & $(c.data) & ")" # there is no default $ for ref objects as there is for objects
var x = C()
var y = x
x.data = 42
echo (x, y) # (C(data: 42), C(data: 42))
Note that:
Nim does not have a copy-on-write mechanism for value types as in Swift (but you can build your own CoW type)
Nim does not have a === operator like Swift (see this discussion).
Can I use PureScript types in JS? For example:
type SomeType = /*type declaration*/
func :: SomeType
func = /*type construction*/
And then in JS do something like this:
let a = module.func()
a instanceof module.SomeType === true
If not if there is any Haskellish language where I can achieve it (Elm, GHCJS, Idris?)?
In general the answer is "no". The runtime representation really depends on what the type is exactly.
For data declarations, every constructor would be represented by a JS "class", like this:
-- PureScript:
data T = A Int | B
x :: T
x = A 42
// JS:
function A(value0) { this.value0 = value0; }
A.create = function(value0) { return new A(value0); }
function B() { }
B.value = new B();
var x = A.create(42);
So you can technically use instanceof to distinguish between cases, but not to determine the type itself.
For a newtype, on the other hand, the type wrapper will be completely erased, because that's the whole point of newtype:
-- PureScript:
newtype T = T Int
x :: T
x = T 42
// JS:
var x = 42;
And for type synonyms (declared with the type keyword), there is no distinct representation. That is, the way such types are represented at runtime is the same as their right-hand side representation:
-- PureScript
type T = Int
x :: T
x = 42
// JS:
var x = 42;
This also applies to naked records, because they are merely type synonyms too:
-- PureScript
type R = { x :: Int, y :: String }
r :: R
r = { x: 42, y: "foo" }
// JS:
var r = { x: 42, y: "foo" }
So the bottom line is: there is no good way to do this. PureScript simply makes no promises about runtime representation. It makes very good promises about program behavior, but not about how it would be implemented in JS (or whatever other backend you happen to be using).
And this is another problem: because PureScript doesn't make any promises like that, it's a very bad idea to rely on your (mine) knowledge of the current implementation, because the next version of the compiler may change it completely. PureScript can do this, because it made no promises. See how that works?
For example, I know there is a pull request in the pipeline somewhere that would switch data representation to arrays, like this:
-- PureScript:
data T = A Int | B
x, y :: T
x = A 42
y = B
// JS:
var x = ["A", 42]
var y = ["B"]
If this pull request is ever accepted, your program will be broken.
In conclusion I can also tell you that neither Elm, nor Haskell, nor Idris will exhibit the property you're after. Runtime type information is generally not a thing valued in the ML universe. One can argue it's effectively present in Idris, in a way, but not in the way you expect.
You may want to try F#/Fable: it's a little bit stronger in this regard, but still not as good as on its native .NET runtime. I don't think you can just check types of F# objects from JS out of the blue, but I know that Fable does support accessing RTTI from F# itself (at extra performance cost), so you can at least make functions to do that and export them to JS.
If you can expand a bit on what your actual problem is, then perhaps I (or someone else) would be able to suggest an alternative solution.
This exercise I made up is intended to help me understand signatures, structures, and functors in Standard ML. I can't seem to get it to work. Just for reference, I'm using
Standard ML of New Jersey v110.75 [built: Sun Jan 20 21:55:21 2013]
I have the following ML signature for an "object for which you can compute magnitude":
signature MAG_OBJ =
sig
type object
val mag : object -> int
end
If I want to give a structure of "int sets with magnitude," I might have a structure for an ordered int to use with the standard library's ORD_SET signature as follows:
structure OrderedInt : ORD_KEY =
struct
type ord_key = int
val compare = Int.compare
end
Then I can create a functor to give me a structure with the desired types and properties:
functor MakeMagSet(structure ELT : ORD_KEY) : MAG_OBJ =
struct
structure Set : ORD_SET = RedBlackSetFn(ELT)
type object = Set.set
val mag = Set.numItems
end
So far so good (everything compiles, at least). Now I create an instance of the structure for my OrderedInt structure I made above:
structure IntMagSet = MakeMagSet(structure ELT = OrderedInt)
But when I try to use it (create a set and compute its magnitude), I get an error:
val X = IntMagSet.Set.addList(IntMagSet.Set.empty, [0,1,2,3,4,5,6,7,8,9])
gives the error:
Error: unbound structure: Set in path IntMagSet.Set.empty.addList
From what I understand, ascribing a signature opaquely using :> makes it so one can't access any structure internals which are not defined explicitly in the signature, but I ascribed MAG_OBJ transparently, so I should be able to access the Set structure, right? What am I missing here?
[EDIT]
Even rewriting the functor to specifically bind the functions I want to the struct is no good:
functor MakeMagSet(structure ELT: ORD_KEY) : MAG_OBJ =
struct
structure Set : ORD_SET = RedBlackSetFn(ELT)
type object = Set.set
val mag = Set.numItems
val empty = Set.empty
val addList = Set.addList
end
Trying to access "empty" and "addList" give unbound variable errors.
On the other hand, trying to explicitly define the Set structure outside of the struct and use its functions gives a type error upon calling mag:
Error: operator and operand don't agree [tycon mismatch]
operator domain: IntMagSet.object
operand: Set.set
in expression:
IntMagSet.mag X
I think it's because you explicitly said that the type of MakeMagSet makes a MAG_OBJ, whose signature does not contain Set. If you had gotten rid of the : MAG_OBJ or made MAG_OBJ include ORD_SET, then it will work.