Getting rid of variant constructors












1















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?










share|improve this question

























  • 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 type t.

    – 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 of objekt because the same rule can be used systematically for all keywords.

    – Martin Jambon
    Nov 20 '18 at 18:04
















1















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?










share|improve this question

























  • 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 type t.

    – 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 of objekt because the same rule can be used systematically for all keywords.

    – Martin Jambon
    Nov 20 '18 at 18:04














1












1








1


0






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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 be Triple.create (Iri iri) iri (Literal literal) for it to have type t.

    – 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 of objekt 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






  • 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 type t.

    – 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 of objekt 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












2 Answers
2






active

oldest

votes


















1














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]).






share|improve this answer


























  • 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



















1














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.






share|improve this answer
























  • 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











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
});


}
});














draft saved

draft discarded


















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









1














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]).






share|improve this answer


























  • 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
















1














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]).






share|improve this answer


























  • 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














1












1








1







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]).






share|improve this answer















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]).







share|improve this answer














share|improve this answer



share|improve this answer








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. 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



















  • 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

















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













1














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.






share|improve this answer
























  • 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
















1














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.






share|improve this answer
























  • 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














1












1








1







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.






share|improve this answer













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.







share|improve this answer












share|improve this answer



share|improve this answer










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



















  • 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


















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Contact image not getting when fetch all contact list from iPhone by CNContact

count number of partitions of a set with n elements into k subsets

A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks