Ji, there!
I want to make a list that contains tuples, such tuples consist of a string and the second element an integer or boolean
I found this answer:
Tuples with different types
But I think it's a bit different, because I have to work with my data already defined, not with Int or Bool, the problem is that I can't put Num (by Int) or B (by Bool) back.
My code
data EAB = Var String
| Num Int
| B Bool
| Sum EAB EAB
| Prod EAB EAB
| Neg EAB
| Pred EAB
| Suc EAB
| And EAB EAB
| Or EAB EAB
| Not EAB
| Iszero EAB
| If EAB EAB EAB
| Let EAB EAB
| Abs String EAB
-- Arithmetic and Boolean expression language
-- Example that I want
-- Ctx = [("x",Num),("y", B),("z", B),("w",Num)]
-- I tried
-- data Type = Num EAB | B EAB
-- type Ctx = [(String, Type)]
-- but doesn't work
This is because I want to define a function that tells me if I can do the evaluation of equal types, based on my EAB, for example
vt :: Ctx → EAB → Type → Bool
--- ...
-- Results
vt [] 4+5 Num = True
vt [] 4+False Num = False
vt [(x,Num)] 4+x Num = True
Thanks!
Simply make a fresh type, with fresh constructors.
data Type = NumT | BT
ctx = [("x", NumT), ("y", BT), ("z", BT), ("w", NumT)]
vt :: Ctx -> EAB -> Type -> Bool
vt [] (Sum (Num 4) (Num 5)) NumT = True -- etc.
You can't make the list you want, but even if you could you can't make your vt function either.
You seem to be intending to use the B and Num constructors of your EAB type to represent a type in your EAB language. That isn't going to be possible.
Constructors are special in Haskell because you can use them in pattern matching. You can write a pattern where you fully apply a constructor to other patterns, and then check whether that matches a value of the pattern's type. Like so:
case expr :: EAB
of Var s -> "Var: " <> s
Num n -> "Num: " <> show n
B b -> "B: " <> show b
Outside of that context constructors are nothing special; they're just functions1. So your Num is a function of type Int -> EAB, and your B is a function of type Bool -> EAB. You cannot compare functions, in any way. The only thing you can do with a function is apply it (to a correctly-typed argument) and see what you get.
This means that your imagined vt is impossible. In that third argument (which you said should be of a type Type that you haven't defined), you've used Num. But remember, Num is just a function of type Int -> EAB. There's no way for the vt function implementation to look at the function it receives and test whether it is Num in order to help decide whether the return value should be True or False. The only thing it could possibly do is apply it to an Int and then inspect the EAB value.
But you presumeably also want to be able to pass B in that position, so that vt can say whether a given EAB expression can be evaluated to a boolean value. B is a function with a different type Bool -> EAB. The only way for the third parameter of vt to accept either type of function is to generalise it with a type variable, making this parameter be (a -> EAB). But then there is no way for vt to know what this function could be applied to, so it won't even be able to do the one thing you can do with functions!2
Basically Num and B cannot be used as abstract representations of the types in your language. They are constructors in the EAB datatype that help you build and inspect values in your language. That is all they are for. Because of that there's really no point going further into why you can't mix Num and B in (tuples inside) a list3 or how you might be able to work around it.
What Daniel Wagner suggested is simply to stop trying to abuse the constructors of EAB for this purpose and just make a datatype to represent types in your language! This is much better anyway because EAB has 15 constructors but your language appears to only have two types.4
So you simply do this:
data EABType = NumT | BT
deriving (Eq, Show)
type Ctx = [(String, EABType)]
-- e.g. [("x",NumT),("y", BT),("z", BT),("w",NumT)
vt :: Ctx -> EAB -> EABType -> Bool
vt ctx expr ty
= case expr of
-- making some guesses about how you want vt to behave
Var s -> (s, ty) `elem` ctx
B _ -> ty == BT
Num _ -> ty == NumT
Sum l r -> ty == NumT && vt ctx l BT && vt ctx r BT
And l r -> ty == BT && vt ctx l BT && vt ctx r BT
...
1 Unless the constructor has no arguments, then it's not a function. You still can't compare them except by pattern matching (an Eq instance is trivially implemented with pattern matching). But a nullary constructor on its own is a complete pattern so there's no such thing as an unapplied nullary constructor that you can't compare.
2 Hypothetically we could use a type class constraint on the variable, which might make it possible to call the function. e.g. If it was (Monoid a => a -> EAB), then we can always call it with mempty. There isn't anything you can do with this idea that will actually help you use this parameter for the purpose you intended though, so I won't go further in this direction.
3 But to hammer the point home: it's the same reason as before. B and Num in the context you've used them are simply functions with different types, you cannot mix differently typed functions in a list, and even if you could you wouldn't be able to do anything with them once they were in the list (like look at each tuple in the list and check which function was there).
4 Assuming you're going for simple value types, and not trying to assign types like Bool -> Bool -> Bool to to unapplied functions.
Related
I'd like make a Set module containing functions. But there seems to be no way to compare functions, which Set needs. This obvious-looking thing compiles:
module Action = struct
type t = unit -> unit
let compare : t -> t -> int = Stdlib.compare
end
module Actions = Set.Make(Action)
But if I attempt to use it:
Fatal error: exception Invalid_argument("compare: functional value")
I just want compare that the functions are the same object, I am not trying to do something silly like compare them for equal behaviour.
Am I supposed to use something from the Obj module here?
The semantics of Stdlib.compare is to look arbitrarily deep inside the objects to see how their subparts compare. This doesn't really work for functions, as you point out. So OCaml doesn't allow compare to be applied to functions.
There isn't really a useful order that can be applied to functions, even if you're willing to use physical equality. Function values are immutable and can be duplicated or moved physically by the runtime system, at least in theory.
Since the ordering would necessarily be arbitrary, you can get the same effect by making your own arbitrary order: pair the functions with an int value that increases monotonically as you create new pairs.
Add an identity to your actions, e.g., you can compare actions by name,
module Action = struct
type t = {
name : string;
func : (unit -> unit);
}
let compare x y = String.compare x.name y.name
end
You should also ensure that all actions have different names, for example by introducing a global hash table that records all created actions. Make sure that actions are only creatable via the Action module, by adding an appropriate signature.
OCaml Stdlib compare documentation states ...
val compare : 'a -> 'a -> int
compare x y returns 0 if x is equal to y, a negative integer if x is less than y, and a positive integer if x is greater than y. The ordering implemented by compare is compatible with the comparison predicates =, < and > defined above, with one difference on the treatment of the float value nan. Namely, the comparison predicates treat nan as different from any other float value, including itself; while compare treats nan as equal to itself and less than any other float value. This treatment of nan ensures that compare defines a total ordering relation.
compare applied to functional values may raise Invalid_argument. compare applied to cyclic structures may not terminate.
The compare function can be used as the comparison function required by the Set.Make and Map.Make functors, as well as the List.sort and Array.sort functions.
Now we have two notion(s) of equality in OCaml, namely...
Structural Equality: Expressed using operator =, the type of which is
# (=);;
- : 'a -> 'a -> bool = <fun>
Physical Equality: Expressed using operator ==, the type of which is
# (==);;
- : 'a -> 'a -> bool = <fun>
As we can see, the type of both is same, but the function application of both of them is different when it comes to function value as arguments.
Structural Equality doesn't hold over function values, but Physical Equality may. Trying to compare(as in =) with function values throws. And as stated in the documentation of Stdlib.compare, it uses structural equality.
Illustration: Structural Equality with Function Values
# let f x = x;;
val f : 'a -> 'a = <fun>
# let g x = x;;
val g : 'a -> 'a = <fun>
# f = g;;
Exception: Invalid_argument "compare: functional value".
# g = f;;
Exception: Invalid_argument "compare: functional value".
Illustration: Physical Equality with Function Values
# let f x = x;;
val f : 'a -> 'a = <fun>
# let g x = x;;
val g : 'a -> 'a = <fun>
# f == g;;
- : bool = false
# f == f;;
- : bool = true
# g == g;;
- : bool = true
# g == f;;
- : bool = false
# let h x y = x + y;;
val h : int -> int -> int = <fun>
# h == f;;
Error: This expression has type int -> int
but an expression was expected of type int -> int -> int
Type int is not compatible with type int -> int
So in short, I don't think we can use Stdlib.compare with Set.Make with function values.
So ...
Either, we will have to keep the type t in Action module to something over which structural equality can be applied if we have to continue using Stdlib.compare.
Or, implement compare of your own so that it can do something with those function values as arguments, along with satisfying the function contract of val compare : t -> t -> int as mandated by Set.OrderedType module type.
WYSIWYG => WHAT YOU SHOW IS WHAT YOU GET
I meet an error about subtyping.
For this code, List.map (fun ((String goal_feat):> Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list).
I meet the following error in vscode:
This expression cannot be coerced to type
Yojson.Basic.t =
[ Assoc of (string * Yojson.Basic.t) list
| Bool of bool
| Float of float
| Int of int
| List of Yojson.Basic.t list
| Null
| String of string ];
it has type [< String of 'a ] -> 'b but is here used with type
[< Yojson.Basic.t ].
While compiling, I meet the following error.
Error: Syntax error: ')' expected.
If I change the code to List.map (fun ((String goal_feat): Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list), which useq explicit type cast instead of subtyping, then the error disappeared. I can not understand what is the problem with my code when i use subtyping. Much appreciation to anyone who could give me some help.
First of all, most likely the answer that you're looking for is
let to_strings xs =
List.map (function `String x -> x | _ -> assert false) (xs :> t list)
The compiler is telling you that your function is handling only one case and you're passing it a list that may contain many other things, so there is a possibility for runtime error. So it is better to indicate to the compiler that you know that only the variants tagged with String are expected. This is what we did in the example above. Now our function has type [> Yojson.Basic.t].
Now back to your direct question. The syntax for coercion is (expr : typeexpr), however in the fun ((String goal_feat):> Basic.t) -> goal_feat snippet, String goal_feat is a pattern, and you cannot coerce a pattern, so we shall use parenthesized pattern here it to give it the right, more general, type1, e.g.,
let exp xs =
List.map (fun (`String x : t) -> x ) (xs :> t list)
This will tell the compiler that the parameter of your function shall belong to a wider type and immediately turn the error into warning 8,
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(`Bool _|`Null|`Assoc _|`List _|`Float _|`Int _)
which says what I was saying in the first part of the post. It is usually a bad idea to leave warning 8 unattended, so I would suggest you to use the first solution, or, otherwise, find a way to prove to the compiler that your list doesn't have any other variants, e.g., you can use List.filter_map for that:
let collect_strings : t list -> [`String of string] list = fun xs ->
List.filter_map (function
| `String s -> Some (`String s)
| _ -> None) xs
And a more natural solution would be to return untagged strings (unless you really need the to be tagged, e.g., when you need to pass this list to a function that is polymorphic over [> t] (Besides, I am using t for Yojson.Basic.t to make the post shorter, but you should use the right name in your code). So here is the solution that will extract strings and make everyone happy (it will throw away values with other tags),
let collect_strings : t list -> string list = fun xs ->
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
Note, that there is no need for type annotations here, and we can easily remove them to get the most general polymoprhic type:
let collect_strings xs =
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
It will get the type
[> `String a] list -> 'a list
which means, a list of polymorphic variants with any tags, returning a list of objects that were tagged with the String tag.
1)It is not a limitation that coercion doesn't work on patterns, moreover it wouldn't make any sense to coerce a pattern. The coercion takes an expression with an existing type and upcasts (weakens) it to a supertype. A function parameter is not an expression, so there is nothing here to coerce. You can just annotate it with the type, e.g., fun (x : #t) -> x will say that our function expects values of type [< t] which is less general than the unannotated type 'a. To summarize, coercion is needed when you have a function that accepts an value that have a object or polymorphic variant type, and in you would like at some expressions to use it with a weakened (upcasted type) for example
type a = [`A]
type b = [`B]
type t = [a | b]
let f : t -> unit = fun _ -> ()
let example : a -> unit = fun x -> f (x :> t)
Here we have type t with two subtypes a and b. Our function f is accepting the base type t, but example is specific to a. In order to be able to use f on an object of type a we need an explicit type coercion to weaken (we lose the type information here) its type to t. Notice that, we do not change the type of x per se, so the following example still type checks:
let rec example : a -> unit = fun x -> f (x :> t); example x
I.e., we weakened the type of the argument to f but the variable x is still having the stronger type a, so we can still use it as a value of type a.
I took a course on OCaml before extensible variant types were introduced, and I don't know much about them. I have several questions:
(This question was deleted because it attracted a "not answerable objectively" close vote.)
What are the low-level consequences of using EVTs, such as performance, memory representation, and (un-)marshaling?
Note that my question is about extensible variant type specifically, unlike the question suggested as identical to this one (that question was asked prior to the introduction of EVTs!).
Extensible variants are quite different from standard variants in term of
runtime behavior.
In particular, extension constructors are runtime values that lives inside
the module where they were defined. For instance, in
type t = ..
module M = struct
type t +=A
end
open M
the second line define a new extension constructor value A and add it to the
existing extension constructors of M at runtime.
Contrarily, classical variants do not really exist at runtime.
It is possible to observe this difference by noticing that I can use
a mli-only compilation unit for classical variants:
(* classical.mli *)
type t = A
(* main.ml *)
let x = Classical.A
and then compile main.ml with
ocamlopt classical.mli main.ml
without troubles because there are no value involved in the Classical module.
Contrarily with extensible variants, this is not possible. If I have
(* ext.mli *)
type t = ..
type t+=A
(* main.ml *)
let x = Ext.A
then the command
ocamlopt ext.mli main.ml
fails with
Error: Required module `Ext' is unavailable
because the runtime value for the extension constructor Ext.A is missing.
You can also peek at both the name and the id of the extension constructor
using the Obj module to see those values
let a = [%extension_constructor A]
Obj.extension_name a;;
: string = "M.A"
Obj.extension_id a;;
: int = 144
(This id is quite brittle and its value it not particurlarly meaningful.)
An important point is that extension constructor are distinguished using their
memory location. Consequently, constructors with n arguments are implemented
as block with n+1 arguments where the first hidden argument is the extension
constructor:
type t += B of int
let x = B 0;;
Here, x contains two fields, and not one:
Obj.size (Obj.repr x);;
: int = 2
And the first field is the extension constructor B:
Obj.field (Obj.repr x) 0 == Obj.repr [%extension_constructor B];;
: bool = true
The previous statement also works for n=0: extensible variants are never
represented as a tagged integer, contrarily to classical variants.
Since marshalling does not preserve physical equality, it means that extensible
sum type cannot be marshalled without losing their identity. For instance, doing
a round trip with
let round_trip (x:'a):'a = Marshall.from_string (Marshall.to_string x []) 0
then testing the result with
type t += C
let is_c = function
| C -> true
| _ -> false
leads to a failure:
is_c (round_trip C)
: bool = false
because the round-trip allocated a new block when reading the marshalled value
This is the same problem which already existed with exceptions, since exceptions
are extensible variants.
This also means that pattern-matching on extensible type is quite different
at runtime. For instance, if I define a simple variant
type s = A of int | B of int
and define a function f as
let f = function
| A n | B n -> n
the compiler is smart enough to optimize this function to simply accessing the
the first field of the argument.
You can check with ocamlc -dlambda that the function above is represented in
the Lambda intermediary representation as:
(function param/1008 (field 0 param/1008)))
However, with extensible variants, not only we need a default pattern
type e = ..
type e += A of n | B of n
let g = function
| A n | B n -> n
| _ -> 0
but we also need to compare the argument with each extension constructor in the
match leading to a more complex lambda IR for the match
(function param/1009
(catch
(if (== (field 0 param/1009) A/1003) (exit 1 (field 1 param/1009))
(if (== (field 0 param/1009) B/1004) (exit 1 (field 1 param/1009))
0))
with (1 n/1007) n/1007)))
Finally, to conclude with an actual example of extensible variants,
in OCaml 4.08, the Format module replaced its string-based user-defined tags
with extensible variants.
This means that defining new tags looks like this:
First, we start with the actual definition of the new tags
type t = Format.stag = ..
type Format.stag += Warning | Error
Then the translation functions for those new tags are
let mark_open_stag tag =
match tag with
| Error -> "\x1b[31m" (* aka print the content of the tag in red *)
| Warning -> "\x1b[35m" (* ... in purple *)
| _ -> ""
let mark_close_stag _tag =
"\x1b[0m" (*reset *)
Installing the new tag is then done with
let enable ppf =
Format.pp_set_tags ppf true;
Format.pp_set_mark_tags ppf true;
Format.pp_set_formatter_stag_functions ppf
{ (Format.pp_get_formatter_stag_functions ppf ()) with
mark_open_stag; mark_close_stag }
With some helper function, printing with those new tags can be done with
Format.printf "This message is %a.#." error "important"
Format.printf "This one %a.#." warning "not so much"
Compared with string tags, there are few advantages:
less room for a spelling mistake
no need to serialize/deserialize potentially complex data
no mix up between different extension constructor with the same name.
chaining multiple user-defined mark_open_stag function is thus safe:
each function can only recognise their own extension constructors.
I want to define a function that accepts an optional argument which is a function ('a -> 'b). The default value should be the identity, which is actually ('a -> 'a), but i see no reason why it should not be compatible with the more general ('a -> 'b). When i try:
let optional_apply ?f i =
match f with
| None -> i + 4
| Some fn -> fn (i + 4)
I always get the narrow type ?f:(int -> int) -> int -> int. But I want to keep f as int -> 'b. What can i do? Or is this just unsound, since optional_apply would not have a definite type? If so, how would I get a similar functionality?
It is impossible, the f argument must not be optional. A simple explanation, is that optional parameters are just a syntactic sugar.
So without sugar your function can be rewritten in a following form:
let optional_apply f n = match f with
| Some f -> f (n + 4)
| None -> (n + 4)
And here typechecker allows us only one type for f: int -> int. If it allowed us an int -> 'a type, then the None path of expression would be unsound. In other words, depending on whether f is None or Some the optional_apply will evaluate to different types. And this is considered unsound.
The best way to prove the unsoundness is to give a simple example where typechecker will allow unsound program:
let f = ref None
let g n = optional_apply ?f:!f n
with this definitions, if type checker allowed f parameter to remain polymorphic, then we can at any time "break" g function by changing f reference to anything else.
This is not compatible with the general type ('a -> 'b). Here's why:
The first pattern, None -> i + 4 is of type int, thus restricting the function's return type to int.
The second pattern, Some fn -> fn (i + 4) must then also be of type int. Because (i + 4) is of type int, too, fn must take and return an int, thus int -> int.
The best alternative I can think of is Haskell's maybe function, of type ('a -> 'b) -> 'b -> 'a option -> 'b:
let maybe fn backup opt = match opt with
| Some v -> fn v
| None -> backup
;;
... which you can probably adapt to your use case.
I am reading the book by Jason, and face the following code.
let x = ref None;;
let one_shot y =
match !x with
None ->
x := Some y;
y
| Some z -> z;;
I do not understand the meaning of Some and None here.
None is something like is not set or null. if a value matches None, this value is not set.
Some is something like is set with something or not null. if a value matches Some z, this value has a value z.
here, the function one_shot looks !x (the variable in address x). if its None then sets with y and returns y and if is Some z then returns z
They are constructors of a built-in OCaml datatype, that you could have defined yourself as such:
type 'a option =
| None
| Some of 'a
This means that None if of type 'a option for any 'a, and, for example, Some 3 is an int option.
Those are constructors for the built-in option type, defined as follows:
type 'a option = None | Some of 'a
It's a generally useful sum type for representing an optional value, used as such in the example shown in your question.
Worth noting here, it's a built-in type (rather than provided in the Pervasives module) because it's used for inference of the types of functions with optional arguments.
For example, consider the following:
let f ?x () =
match x with
| Some x -> x
| None -> 0
This function has the following type:
val f: ?x:int -> unit -> int