Getting rid of variant constructors
As a side project, I try to implement the basics of an RDF library in OCaml.
As you may (or may not) know, a RDF statement (or triple) is composed of 3 parts:
- The subject can be an IRI or a blank node;
- The predicate must be an IRI;
- The object can be an IRI, a blank node or a literal.
I have module and types for IRIs, blank nodes and literals, and in order to type-proof the rules described above, here is what I've started to write:
(* In `triple.ml` *)
type subject = Iri of Iri.t | Bnode of Bnode.t
type objekt = Iri of Iri.t | Bnode of Bnode.t | Literal of Literal.t
type t = subject * Iri.t * objekt
let create s p o = s, p, o
So this is nice and everything, but one thing grinds my gears: whenever I want to use Triple.create
, I must explicitly state the constructor of the variant:
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
Triple.create (Iri iri) iri (Literal literal)
I'm pretty sure OCaml has ways to work around that, but I'm not sure how.
Some thoughts: I could parameterize the Triple.t
type with the type of its subject and the type of its object, but then how do I enforce the restrictions on the parameter types? Or maybe it is a good use case for a GADT?
ocaml variant
add a comment |
As a side project, I try to implement the basics of an RDF library in OCaml.
As you may (or may not) know, a RDF statement (or triple) is composed of 3 parts:
- The subject can be an IRI or a blank node;
- The predicate must be an IRI;
- The object can be an IRI, a blank node or a literal.
I have module and types for IRIs, blank nodes and literals, and in order to type-proof the rules described above, here is what I've started to write:
(* In `triple.ml` *)
type subject = Iri of Iri.t | Bnode of Bnode.t
type objekt = Iri of Iri.t | Bnode of Bnode.t | Literal of Literal.t
type t = subject * Iri.t * objekt
let create s p o = s, p, o
So this is nice and everything, but one thing grinds my gears: whenever I want to use Triple.create
, I must explicitly state the constructor of the variant:
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
Triple.create (Iri iri) iri (Literal literal)
I'm pretty sure OCaml has ways to work around that, but I'm not sure how.
Some thoughts: I could parameterize the Triple.t
type with the type of its subject and the type of its object, but then how do I enforce the restrictions on the parameter types? Or maybe it is a good use case for a GADT?
ocaml variant
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
1
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
It looks like the example should beTriple.create (Iri iri) iri (Literal literal)
for it to have typet
.
– Martin Jambon
Nov 20 '18 at 17:58
1
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
1
Another small remark: I'd useobject_
instead ofobjekt
because the same rule can be used systematically for all keywords.
– Martin Jambon
Nov 20 '18 at 18:04
add a comment |
As a side project, I try to implement the basics of an RDF library in OCaml.
As you may (or may not) know, a RDF statement (or triple) is composed of 3 parts:
- The subject can be an IRI or a blank node;
- The predicate must be an IRI;
- The object can be an IRI, a blank node or a literal.
I have module and types for IRIs, blank nodes and literals, and in order to type-proof the rules described above, here is what I've started to write:
(* In `triple.ml` *)
type subject = Iri of Iri.t | Bnode of Bnode.t
type objekt = Iri of Iri.t | Bnode of Bnode.t | Literal of Literal.t
type t = subject * Iri.t * objekt
let create s p o = s, p, o
So this is nice and everything, but one thing grinds my gears: whenever I want to use Triple.create
, I must explicitly state the constructor of the variant:
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
Triple.create (Iri iri) iri (Literal literal)
I'm pretty sure OCaml has ways to work around that, but I'm not sure how.
Some thoughts: I could parameterize the Triple.t
type with the type of its subject and the type of its object, but then how do I enforce the restrictions on the parameter types? Or maybe it is a good use case for a GADT?
ocaml variant
As a side project, I try to implement the basics of an RDF library in OCaml.
As you may (or may not) know, a RDF statement (or triple) is composed of 3 parts:
- The subject can be an IRI or a blank node;
- The predicate must be an IRI;
- The object can be an IRI, a blank node or a literal.
I have module and types for IRIs, blank nodes and literals, and in order to type-proof the rules described above, here is what I've started to write:
(* In `triple.ml` *)
type subject = Iri of Iri.t | Bnode of Bnode.t
type objekt = Iri of Iri.t | Bnode of Bnode.t | Literal of Literal.t
type t = subject * Iri.t * objekt
let create s p o = s, p, o
So this is nice and everything, but one thing grinds my gears: whenever I want to use Triple.create
, I must explicitly state the constructor of the variant:
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
Triple.create (Iri iri) iri (Literal literal)
I'm pretty sure OCaml has ways to work around that, but I'm not sure how.
Some thoughts: I could parameterize the Triple.t
type with the type of its subject and the type of its object, but then how do I enforce the restrictions on the parameter types? Or maybe it is a good use case for a GADT?
ocaml variant
ocaml variant
edited Nov 21 '18 at 16:42
Richard-Degenne
asked Nov 19 '18 at 22:15
Richard-DegenneRichard-Degenne
1,82411632
1,82411632
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
1
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
It looks like the example should beTriple.create (Iri iri) iri (Literal literal)
for it to have typet
.
– Martin Jambon
Nov 20 '18 at 17:58
1
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
1
Another small remark: I'd useobject_
instead ofobjekt
because the same rule can be used systematically for all keywords.
– Martin Jambon
Nov 20 '18 at 18:04
add a comment |
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
1
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
It looks like the example should beTriple.create (Iri iri) iri (Literal literal)
for it to have typet
.
– Martin Jambon
Nov 20 '18 at 17:58
1
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
1
Another small remark: I'd useobject_
instead ofobjekt
because the same rule can be used systematically for all keywords.
– Martin Jambon
Nov 20 '18 at 18:04
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
1
1
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
It looks like the example should be
Triple.create (Iri iri) iri (Literal literal)
for it to have type t
.– Martin Jambon
Nov 20 '18 at 17:58
It looks like the example should be
Triple.create (Iri iri) iri (Literal literal)
for it to have type t
.– Martin Jambon
Nov 20 '18 at 17:58
1
1
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
1
1
Another small remark: I'd use
object_
instead of objekt
because the same rule can be used systematically for all keywords.– Martin Jambon
Nov 20 '18 at 18:04
Another small remark: I'd use
object_
instead of objekt
because the same rule can be used systematically for all keywords.– Martin Jambon
Nov 20 '18 at 18:04
add a comment |
2 Answers
2
active
oldest
votes
If you don't mind changing the types of Iri.t
, etc, you could do something like this (replacing internal = string
with the real type in each case):
module Iri : sig
type internal
type t = [`Iri of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Iri of internal]
let v x = `Iri x
end
module Bnode : sig
type internal
type t = [`Bnode of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Bnode of internal]
let v x = `Bnode x
end
module Literal : sig
type internal
type t = [`Literal of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Literal of internal]
let v x = `Literal x
end
module Triple = struct
type subject = [Iri.t | Bnode.t]
type objekt = [Iri.t | Bnode.t | Literal.t]
type t = subject * Iri.t * objekt
let v s p o : t = s, p, o
end
let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"
let x1 = Bnode.v "blank-x1"
let foo = Literal.v "foo"
let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo
Note that in the example at the end, the same value bob
is used both as a subject ([Iri.t | Bnode.t]
) and as an object ([Iri.t | Bnode.t | Literal.t]
).
Thanks for your answer, this is very helpful. ForBnode
andLiteral
, it works like a charm. ForIri
however, I simply include theUri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?
– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to setIri.internal = Uri.t
(and users would need to useIri.v uri
to create anIri
from aUri.t
).
– Thomas Leonard
Nov 27 '18 at 21:08
add a comment |
I'm not sure how you can fully achieve this even with GADT. What will be the type of create
in this case? First argument must be either Iri.t
or Bnode.t
unless one is a subtype of another, you can't write such function (or it will be very general: 'a -> ...
).
In any case you need to provide a type of the arguments. What you can do with GADT is to "move" the information about the types into another type:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
encode the types of the first and third arguments of create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
But I really doubt that this is a better version than the original one.
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53383421%2fgetting-rid-of-variant-constructors%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
If you don't mind changing the types of Iri.t
, etc, you could do something like this (replacing internal = string
with the real type in each case):
module Iri : sig
type internal
type t = [`Iri of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Iri of internal]
let v x = `Iri x
end
module Bnode : sig
type internal
type t = [`Bnode of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Bnode of internal]
let v x = `Bnode x
end
module Literal : sig
type internal
type t = [`Literal of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Literal of internal]
let v x = `Literal x
end
module Triple = struct
type subject = [Iri.t | Bnode.t]
type objekt = [Iri.t | Bnode.t | Literal.t]
type t = subject * Iri.t * objekt
let v s p o : t = s, p, o
end
let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"
let x1 = Bnode.v "blank-x1"
let foo = Literal.v "foo"
let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo
Note that in the example at the end, the same value bob
is used both as a subject ([Iri.t | Bnode.t]
) and as an object ([Iri.t | Bnode.t | Literal.t]
).
Thanks for your answer, this is very helpful. ForBnode
andLiteral
, it works like a charm. ForIri
however, I simply include theUri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?
– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to setIri.internal = Uri.t
(and users would need to useIri.v uri
to create anIri
from aUri.t
).
– Thomas Leonard
Nov 27 '18 at 21:08
add a comment |
If you don't mind changing the types of Iri.t
, etc, you could do something like this (replacing internal = string
with the real type in each case):
module Iri : sig
type internal
type t = [`Iri of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Iri of internal]
let v x = `Iri x
end
module Bnode : sig
type internal
type t = [`Bnode of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Bnode of internal]
let v x = `Bnode x
end
module Literal : sig
type internal
type t = [`Literal of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Literal of internal]
let v x = `Literal x
end
module Triple = struct
type subject = [Iri.t | Bnode.t]
type objekt = [Iri.t | Bnode.t | Literal.t]
type t = subject * Iri.t * objekt
let v s p o : t = s, p, o
end
let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"
let x1 = Bnode.v "blank-x1"
let foo = Literal.v "foo"
let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo
Note that in the example at the end, the same value bob
is used both as a subject ([Iri.t | Bnode.t]
) and as an object ([Iri.t | Bnode.t | Literal.t]
).
Thanks for your answer, this is very helpful. ForBnode
andLiteral
, it works like a charm. ForIri
however, I simply include theUri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?
– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to setIri.internal = Uri.t
(and users would need to useIri.v uri
to create anIri
from aUri.t
).
– Thomas Leonard
Nov 27 '18 at 21:08
add a comment |
If you don't mind changing the types of Iri.t
, etc, you could do something like this (replacing internal = string
with the real type in each case):
module Iri : sig
type internal
type t = [`Iri of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Iri of internal]
let v x = `Iri x
end
module Bnode : sig
type internal
type t = [`Bnode of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Bnode of internal]
let v x = `Bnode x
end
module Literal : sig
type internal
type t = [`Literal of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Literal of internal]
let v x = `Literal x
end
module Triple = struct
type subject = [Iri.t | Bnode.t]
type objekt = [Iri.t | Bnode.t | Literal.t]
type t = subject * Iri.t * objekt
let v s p o : t = s, p, o
end
let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"
let x1 = Bnode.v "blank-x1"
let foo = Literal.v "foo"
let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo
Note that in the example at the end, the same value bob
is used both as a subject ([Iri.t | Bnode.t]
) and as an object ([Iri.t | Bnode.t | Literal.t]
).
If you don't mind changing the types of Iri.t
, etc, you could do something like this (replacing internal = string
with the real type in each case):
module Iri : sig
type internal
type t = [`Iri of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Iri of internal]
let v x = `Iri x
end
module Bnode : sig
type internal
type t = [`Bnode of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Bnode of internal]
let v x = `Bnode x
end
module Literal : sig
type internal
type t = [`Literal of internal]
val v : string -> [> t]
end = struct
type internal = string
type t = [`Literal of internal]
let v x = `Literal x
end
module Triple = struct
type subject = [Iri.t | Bnode.t]
type objekt = [Iri.t | Bnode.t | Literal.t]
type t = subject * Iri.t * objekt
let v s p o : t = s, p, o
end
let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"
let x1 = Bnode.v "blank-x1"
let foo = Literal.v "foo"
let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo
Note that in the example at the end, the same value bob
is used both as a subject ([Iri.t | Bnode.t]
) and as an object ([Iri.t | Bnode.t | Literal.t]
).
edited Nov 27 '18 at 21:09
answered Nov 24 '18 at 17:51
Thomas LeonardThomas Leonard
5,48322632
5,48322632
Thanks for your answer, this is very helpful. ForBnode
andLiteral
, it works like a charm. ForIri
however, I simply include theUri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?
– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to setIri.internal = Uri.t
(and users would need to useIri.v uri
to create anIri
from aUri.t
).
– Thomas Leonard
Nov 27 '18 at 21:08
add a comment |
Thanks for your answer, this is very helpful. ForBnode
andLiteral
, it works like a charm. ForIri
however, I simply include theUri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?
– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to setIri.internal = Uri.t
(and users would need to useIri.v uri
to create anIri
from aUri.t
).
– Thomas Leonard
Nov 27 '18 at 21:08
Thanks for your answer, this is very helpful. For
Bnode
and Literal
, it works like a charm. For Iri
however, I simply include the Uri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?– Richard-Degenne
Nov 26 '18 at 20:12
Thanks for your answer, this is very helpful. For
Bnode
and Literal
, it works like a charm. For Iri
however, I simply include the Uri
module from the ocaml-uri package (details here). Is there a way to connect the library with this new type definition without having to redefine everything?– Richard-Degenne
Nov 26 '18 at 20:12
You'd have to set
Iri.internal = Uri.t
(and users would need to use Iri.v uri
to create an Iri
from a Uri.t
).– Thomas Leonard
Nov 27 '18 at 21:08
You'd have to set
Iri.internal = Uri.t
(and users would need to use Iri.v uri
to create an Iri
from a Uri.t
).– Thomas Leonard
Nov 27 '18 at 21:08
add a comment |
I'm not sure how you can fully achieve this even with GADT. What will be the type of create
in this case? First argument must be either Iri.t
or Bnode.t
unless one is a subtype of another, you can't write such function (or it will be very general: 'a -> ...
).
In any case you need to provide a type of the arguments. What you can do with GADT is to "move" the information about the types into another type:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
encode the types of the first and third arguments of create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
But I really doubt that this is a better version than the original one.
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
add a comment |
I'm not sure how you can fully achieve this even with GADT. What will be the type of create
in this case? First argument must be either Iri.t
or Bnode.t
unless one is a subtype of another, you can't write such function (or it will be very general: 'a -> ...
).
In any case you need to provide a type of the arguments. What you can do with GADT is to "move" the information about the types into another type:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
encode the types of the first and third arguments of create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
But I really doubt that this is a better version than the original one.
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
add a comment |
I'm not sure how you can fully achieve this even with GADT. What will be the type of create
in this case? First argument must be either Iri.t
or Bnode.t
unless one is a subtype of another, you can't write such function (or it will be very general: 'a -> ...
).
In any case you need to provide a type of the arguments. What you can do with GADT is to "move" the information about the types into another type:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
encode the types of the first and third arguments of create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
But I really doubt that this is a better version than the original one.
I'm not sure how you can fully achieve this even with GADT. What will be the type of create
in this case? First argument must be either Iri.t
or Bnode.t
unless one is a subtype of another, you can't write such function (or it will be very general: 'a -> ...
).
In any case you need to provide a type of the arguments. What you can do with GADT is to "move" the information about the types into another type:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
encode the types of the first and third arguments of create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
But I really doubt that this is a better version than the original one.
answered Nov 20 '18 at 8:56
vonakavonaka
6791716
6791716
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
add a comment |
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
Yeah, I tried toying around with GADT, and I came to the same conclusions as you.
– Richard-Degenne
Nov 20 '18 at 9:49
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
GADTs don't eliminate any Constructors but add more strict type requirements. GADTs would only make sense if the types had some dependencies, e.g. subject and object must both be Iri or both be Bnode and such.
– Goswin von Brederlow
Nov 23 '18 at 15:16
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53383421%2fgetting-rid-of-variant-constructors%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Reading back this article I had found a while back, it seems that GADT is the way to go here. I've never used them before, so this should be interesting.
– Richard-Degenne
Nov 19 '18 at 22:29
1
Not sure, maybe you can use inheritance instead of GADTs. I've never used OOP in Ocaml though.
– Bergi
Nov 20 '18 at 8:57
It looks like the example should be
Triple.create (Iri iri) iri (Literal literal)
for it to have typet
.– Martin Jambon
Nov 20 '18 at 17:58
1
Here I recommend using a record rather than a tuple. There's no downside to it since you have a type definition already. It also has the advantage of being easily extensible (you can add a field without breaking too much code) and it's easy to access just one field without having to know about the position or number of other fields.
– Martin Jambon
Nov 20 '18 at 18:02
1
Another small remark: I'd use
object_
instead ofobjekt
because the same rule can be used systematically for all keywords.– Martin Jambon
Nov 20 '18 at 18:04