Using GADT variants in a polymorphic, compound type as I would normal algebraic variant types?
Let's say I have this simple variant-type:
type flag = {
name: string;
payload: string option;
}
type word =
| Arg of string
| Flag of flag
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
If, however, I want to add GADT-constraints to that word
type,
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
… the compiler can no longer infer a general type for the members of args
:
Line 12, 3:
This expression has type flag word
but an expression was expected of type string word
Type flag is not compatible with type string
Is this simply a limitation of GADTs?
polymorphism ocaml algebraic-data-types gadt
add a comment |
Let's say I have this simple variant-type:
type flag = {
name: string;
payload: string option;
}
type word =
| Arg of string
| Flag of flag
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
If, however, I want to add GADT-constraints to that word
type,
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
… the compiler can no longer infer a general type for the members of args
:
Line 12, 3:
This expression has type flag word
but an expression was expected of type string word
Type flag is not compatible with type string
Is this simply a limitation of GADTs?
polymorphism ocaml algebraic-data-types gadt
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55
add a comment |
Let's say I have this simple variant-type:
type flag = {
name: string;
payload: string option;
}
type word =
| Arg of string
| Flag of flag
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
If, however, I want to add GADT-constraints to that word
type,
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
… the compiler can no longer infer a general type for the members of args
:
Line 12, 3:
This expression has type flag word
but an expression was expected of type string word
Type flag is not compatible with type string
Is this simply a limitation of GADTs?
polymorphism ocaml algebraic-data-types gadt
Let's say I have this simple variant-type:
type flag = {
name: string;
payload: string option;
}
type word =
| Arg of string
| Flag of flag
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
If, however, I want to add GADT-constraints to that word
type,
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
… the compiler can no longer infer a general type for the members of args
:
Line 12, 3:
This expression has type flag word
but an expression was expected of type string word
Type flag is not compatible with type string
Is this simply a limitation of GADTs?
polymorphism ocaml algebraic-data-types gadt
polymorphism ocaml algebraic-data-types gadt
edited Nov 21 '18 at 0:24
glennsl
9,914102848
9,914102848
asked Nov 20 '18 at 23:26
ELLIOTTCABLEELLIOTTCABLE
10.4k84261
10.4k84261
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55
add a comment |
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55
add a comment |
3 Answers
3
active
oldest
votes
I'm certainly no expert on GADTs, but this seems more like a limitation of ordinary type variables than of GADTs. flag word
and string word
are distinct types that won't fit into the same array no matter what.
I assume what you want is an existentially quantified type variable. If so the existential type variable should only occur on the left side of the arrow. This seems to work:
type word =
| Arg : string -> word
| Flag : flag -> word
Or in the words of the manual:
Variables are made existential when they appear inside a constructor’s argument, but not in its return type.
Edit: Nope, as @octachron points out in a comment, this is not how existential types work. Or rather existential type variables, of which there are none in my example. The cause is the same, but I'm not entirely clear on what property of a GADT is desired here and what therefore the solution would be.
Note that your typeword
is equivalent to the classical varianttype word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin totype dyn_word = Dyn: 'a word -> dyn_word
where'a
is existentially quantified .
– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
add a comment |
It is not so much a limitation of GADTs than one of their desired properties. When you write
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
you are asking the typechecker to make the type of Arg _
and Flag _
different and incompatible.
To motivate this behavior, a better example might be a list with a static length:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| : ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
With this definition a value of type ('n, 's) nlist
carries an encoding of its length inside this type. This makes it possible to write a total hd
function
let hd (a::q) = a
Since our exotic list type carries its length in its type, the typechecker can express the fact that hd
only accepts list with one or more argument (i.e. the type of hd is
('elt,_ succ) nlist -> 'elt
). Thus the function hd
always returns (when its type checks).
But this also means that the type checker must now enforce than the function hd
always works. In other words, an array mixing length of different size
[| ; [1]; [1;2] |]
cannot be allowed to type-check because it contains element for which the hd
function is not well-defined and the type checker guaranteed to us earlier than hd
will always return a successful value.
Returning to your example, with your type definition, you have made it possible to discriminate between Flag _
and Int _
. Thus I can write the following total function
let empty (Arg _) = ""
let map_empty = Array.map empty
and expect than map_empty
works on all well-typed arrays. But I cannot possibly apply this function to your mixed array
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
In other words, this array cannot be well-typed.
add a comment |
The usual way to handle this situation with GADTs is to introduce existential wrappers (here, any_word
):
type flag = {
name : string;
payload : string option;
}
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
type any_word = Word : _ word -> any_word [@@unboxed]
let args = [|
Word (Arg "hello");
Word (Flag {name = "foo"; payload = Some "world"});
|]
args
contains word
s of unknown type index, that index being hidden inside the any_word
wrapper. In contrast,
let flag_args = [|
Flag {name = "zonk"; payload = None};
Flag {name = "splines"; payload = Some "reticulate"};
|]
flag_args
contains flag word
s specifically. string word
s are excluded by the type system, which is one of the attractions of GADTs. (If you don't need this, GADTs are not likely to be helpful.)
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%2f53403143%2fusing-gadt-variants-in-a-polymorphic-compound-type-as-i-would-normal-algebraic%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
I'm certainly no expert on GADTs, but this seems more like a limitation of ordinary type variables than of GADTs. flag word
and string word
are distinct types that won't fit into the same array no matter what.
I assume what you want is an existentially quantified type variable. If so the existential type variable should only occur on the left side of the arrow. This seems to work:
type word =
| Arg : string -> word
| Flag : flag -> word
Or in the words of the manual:
Variables are made existential when they appear inside a constructor’s argument, but not in its return type.
Edit: Nope, as @octachron points out in a comment, this is not how existential types work. Or rather existential type variables, of which there are none in my example. The cause is the same, but I'm not entirely clear on what property of a GADT is desired here and what therefore the solution would be.
Note that your typeword
is equivalent to the classical varianttype word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin totype dyn_word = Dyn: 'a word -> dyn_word
where'a
is existentially quantified .
– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
add a comment |
I'm certainly no expert on GADTs, but this seems more like a limitation of ordinary type variables than of GADTs. flag word
and string word
are distinct types that won't fit into the same array no matter what.
I assume what you want is an existentially quantified type variable. If so the existential type variable should only occur on the left side of the arrow. This seems to work:
type word =
| Arg : string -> word
| Flag : flag -> word
Or in the words of the manual:
Variables are made existential when they appear inside a constructor’s argument, but not in its return type.
Edit: Nope, as @octachron points out in a comment, this is not how existential types work. Or rather existential type variables, of which there are none in my example. The cause is the same, but I'm not entirely clear on what property of a GADT is desired here and what therefore the solution would be.
Note that your typeword
is equivalent to the classical varianttype word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin totype dyn_word = Dyn: 'a word -> dyn_word
where'a
is existentially quantified .
– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
add a comment |
I'm certainly no expert on GADTs, but this seems more like a limitation of ordinary type variables than of GADTs. flag word
and string word
are distinct types that won't fit into the same array no matter what.
I assume what you want is an existentially quantified type variable. If so the existential type variable should only occur on the left side of the arrow. This seems to work:
type word =
| Arg : string -> word
| Flag : flag -> word
Or in the words of the manual:
Variables are made existential when they appear inside a constructor’s argument, but not in its return type.
Edit: Nope, as @octachron points out in a comment, this is not how existential types work. Or rather existential type variables, of which there are none in my example. The cause is the same, but I'm not entirely clear on what property of a GADT is desired here and what therefore the solution would be.
I'm certainly no expert on GADTs, but this seems more like a limitation of ordinary type variables than of GADTs. flag word
and string word
are distinct types that won't fit into the same array no matter what.
I assume what you want is an existentially quantified type variable. If so the existential type variable should only occur on the left side of the arrow. This seems to work:
type word =
| Arg : string -> word
| Flag : flag -> word
Or in the words of the manual:
Variables are made existential when they appear inside a constructor’s argument, but not in its return type.
Edit: Nope, as @octachron points out in a comment, this is not how existential types work. Or rather existential type variables, of which there are none in my example. The cause is the same, but I'm not entirely clear on what property of a GADT is desired here and what therefore the solution would be.
edited Nov 21 '18 at 10:03
answered Nov 21 '18 at 0:17
glennslglennsl
9,914102848
9,914102848
Note that your typeword
is equivalent to the classical varianttype word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin totype dyn_word = Dyn: 'a word -> dyn_word
where'a
is existentially quantified .
– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
add a comment |
Note that your typeword
is equivalent to the classical varianttype word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin totype dyn_word = Dyn: 'a word -> dyn_word
where'a
is existentially quantified .
– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
Note that your type
word
is equivalent to the classical variant type word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin to type dyn_word = Dyn: 'a word -> dyn_word
where 'a
is existentially quantified .– octachron
Nov 21 '18 at 8:45
Note that your type
word
is equivalent to the classical variant type word = Arg of string | Flag of flag
since there is no type variables in the constructor definitions. An existential quantification would be more akin to type dyn_word = Dyn: 'a word -> dyn_word
where 'a
is existentially quantified .– octachron
Nov 21 '18 at 8:45
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
@octachron Right, lol, of course :) It's not entirely clear to me what purpose OP had in mind for using a GADT then.
– glennsl
Nov 21 '18 at 10:00
1
1
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
See the post — I started with a classical variant, and tried to add a GADT constraint to it! As for the goal, it's simply to experiment with GADTs, start to get a feel for when I would use them in practice, see what restrictions they add. No particular goal. 😱
– ELLIOTTCABLE
Nov 21 '18 at 17:25
add a comment |
It is not so much a limitation of GADTs than one of their desired properties. When you write
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
you are asking the typechecker to make the type of Arg _
and Flag _
different and incompatible.
To motivate this behavior, a better example might be a list with a static length:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| : ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
With this definition a value of type ('n, 's) nlist
carries an encoding of its length inside this type. This makes it possible to write a total hd
function
let hd (a::q) = a
Since our exotic list type carries its length in its type, the typechecker can express the fact that hd
only accepts list with one or more argument (i.e. the type of hd is
('elt,_ succ) nlist -> 'elt
). Thus the function hd
always returns (when its type checks).
But this also means that the type checker must now enforce than the function hd
always works. In other words, an array mixing length of different size
[| ; [1]; [1;2] |]
cannot be allowed to type-check because it contains element for which the hd
function is not well-defined and the type checker guaranteed to us earlier than hd
will always return a successful value.
Returning to your example, with your type definition, you have made it possible to discriminate between Flag _
and Int _
. Thus I can write the following total function
let empty (Arg _) = ""
let map_empty = Array.map empty
and expect than map_empty
works on all well-typed arrays. But I cannot possibly apply this function to your mixed array
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
In other words, this array cannot be well-typed.
add a comment |
It is not so much a limitation of GADTs than one of their desired properties. When you write
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
you are asking the typechecker to make the type of Arg _
and Flag _
different and incompatible.
To motivate this behavior, a better example might be a list with a static length:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| : ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
With this definition a value of type ('n, 's) nlist
carries an encoding of its length inside this type. This makes it possible to write a total hd
function
let hd (a::q) = a
Since our exotic list type carries its length in its type, the typechecker can express the fact that hd
only accepts list with one or more argument (i.e. the type of hd is
('elt,_ succ) nlist -> 'elt
). Thus the function hd
always returns (when its type checks).
But this also means that the type checker must now enforce than the function hd
always works. In other words, an array mixing length of different size
[| ; [1]; [1;2] |]
cannot be allowed to type-check because it contains element for which the hd
function is not well-defined and the type checker guaranteed to us earlier than hd
will always return a successful value.
Returning to your example, with your type definition, you have made it possible to discriminate between Flag _
and Int _
. Thus I can write the following total function
let empty (Arg _) = ""
let map_empty = Array.map empty
and expect than map_empty
works on all well-typed arrays. But I cannot possibly apply this function to your mixed array
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
In other words, this array cannot be well-typed.
add a comment |
It is not so much a limitation of GADTs than one of their desired properties. When you write
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
you are asking the typechecker to make the type of Arg _
and Flag _
different and incompatible.
To motivate this behavior, a better example might be a list with a static length:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| : ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
With this definition a value of type ('n, 's) nlist
carries an encoding of its length inside this type. This makes it possible to write a total hd
function
let hd (a::q) = a
Since our exotic list type carries its length in its type, the typechecker can express the fact that hd
only accepts list with one or more argument (i.e. the type of hd is
('elt,_ succ) nlist -> 'elt
). Thus the function hd
always returns (when its type checks).
But this also means that the type checker must now enforce than the function hd
always works. In other words, an array mixing length of different size
[| ; [1]; [1;2] |]
cannot be allowed to type-check because it contains element for which the hd
function is not well-defined and the type checker guaranteed to us earlier than hd
will always return a successful value.
Returning to your example, with your type definition, you have made it possible to discriminate between Flag _
and Int _
. Thus I can write the following total function
let empty (Arg _) = ""
let map_empty = Array.map empty
and expect than map_empty
works on all well-typed arrays. But I cannot possibly apply this function to your mixed array
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
In other words, this array cannot be well-typed.
It is not so much a limitation of GADTs than one of their desired properties. When you write
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
you are asking the typechecker to make the type of Arg _
and Flag _
different and incompatible.
To motivate this behavior, a better example might be a list with a static length:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| : ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
With this definition a value of type ('n, 's) nlist
carries an encoding of its length inside this type. This makes it possible to write a total hd
function
let hd (a::q) = a
Since our exotic list type carries its length in its type, the typechecker can express the fact that hd
only accepts list with one or more argument (i.e. the type of hd is
('elt,_ succ) nlist -> 'elt
). Thus the function hd
always returns (when its type checks).
But this also means that the type checker must now enforce than the function hd
always works. In other words, an array mixing length of different size
[| ; [1]; [1;2] |]
cannot be allowed to type-check because it contains element for which the hd
function is not well-defined and the type checker guaranteed to us earlier than hd
will always return a successful value.
Returning to your example, with your type definition, you have made it possible to discriminate between Flag _
and Int _
. Thus I can write the following total function
let empty (Arg _) = ""
let map_empty = Array.map empty
and expect than map_empty
works on all well-typed arrays. But I cannot possibly apply this function to your mixed array
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
In other words, this array cannot be well-typed.
edited Nov 22 '18 at 12:29
answered Nov 22 '18 at 8:16
octachronoctachron
4,4281513
4,4281513
add a comment |
add a comment |
The usual way to handle this situation with GADTs is to introduce existential wrappers (here, any_word
):
type flag = {
name : string;
payload : string option;
}
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
type any_word = Word : _ word -> any_word [@@unboxed]
let args = [|
Word (Arg "hello");
Word (Flag {name = "foo"; payload = Some "world"});
|]
args
contains word
s of unknown type index, that index being hidden inside the any_word
wrapper. In contrast,
let flag_args = [|
Flag {name = "zonk"; payload = None};
Flag {name = "splines"; payload = Some "reticulate"};
|]
flag_args
contains flag word
s specifically. string word
s are excluded by the type system, which is one of the attractions of GADTs. (If you don't need this, GADTs are not likely to be helpful.)
add a comment |
The usual way to handle this situation with GADTs is to introduce existential wrappers (here, any_word
):
type flag = {
name : string;
payload : string option;
}
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
type any_word = Word : _ word -> any_word [@@unboxed]
let args = [|
Word (Arg "hello");
Word (Flag {name = "foo"; payload = Some "world"});
|]
args
contains word
s of unknown type index, that index being hidden inside the any_word
wrapper. In contrast,
let flag_args = [|
Flag {name = "zonk"; payload = None};
Flag {name = "splines"; payload = Some "reticulate"};
|]
flag_args
contains flag word
s specifically. string word
s are excluded by the type system, which is one of the attractions of GADTs. (If you don't need this, GADTs are not likely to be helpful.)
add a comment |
The usual way to handle this situation with GADTs is to introduce existential wrappers (here, any_word
):
type flag = {
name : string;
payload : string option;
}
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
type any_word = Word : _ word -> any_word [@@unboxed]
let args = [|
Word (Arg "hello");
Word (Flag {name = "foo"; payload = Some "world"});
|]
args
contains word
s of unknown type index, that index being hidden inside the any_word
wrapper. In contrast,
let flag_args = [|
Flag {name = "zonk"; payload = None};
Flag {name = "splines"; payload = Some "reticulate"};
|]
flag_args
contains flag word
s specifically. string word
s are excluded by the type system, which is one of the attractions of GADTs. (If you don't need this, GADTs are not likely to be helpful.)
The usual way to handle this situation with GADTs is to introduce existential wrappers (here, any_word
):
type flag = {
name : string;
payload : string option;
}
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
type any_word = Word : _ word -> any_word [@@unboxed]
let args = [|
Word (Arg "hello");
Word (Flag {name = "foo"; payload = Some "world"});
|]
args
contains word
s of unknown type index, that index being hidden inside the any_word
wrapper. In contrast,
let flag_args = [|
Flag {name = "zonk"; payload = None};
Flag {name = "splines"; payload = Some "reticulate"};
|]
flag_args
contains flag word
s specifically. string word
s are excluded by the type system, which is one of the attractions of GADTs. (If you don't need this, GADTs are not likely to be helpful.)
answered Nov 23 '18 at 7:02
gsggsg
7,82911118
7,82911118
add a comment |
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%2f53403143%2fusing-gadt-variants-in-a-polymorphic-compound-type-as-i-would-normal-algebraic%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
you just don't need GADTs in your case. It is not a limitation of GADTs, but a feature :) Look at that experiment (building type safe evaluator) stackoverflow.com/questions/30429552/…
– Stas
Nov 22 '18 at 6:55