I have the following, which works up until the point I try to define readJSON':
newtype JSONWithErr a = JSONWithErr (Writer (Array Foreign.ForeignError) a)
derive newtype instance jsonWithErrApply :: Apply JSONWithErr
derive newtype instance jsonWithErrApplicative :: Applicative JSONWithErr
derive newtype instance jsonWithErrFunctor :: Functor JSONWithErr
derive newtype instance jsonWithErrBind :: Bind JSONWithErr
derive newtype instance jsonWithErrMonad :: Monad JSONWithErr
derive newtype instance jsonWithErrTell :: MonadTell (Array Foreign.ForeignError) JSONWithErr
derive newtype instance jsonWithErrWriter :: MonadWriter (Array Foreign.ForeignError) JSONWithErr
newtype JSONParse a = JSONParse (ExceptT (NonEmptyList ForeignError) JSONWithErr a)
derive newtype instance jsonParseApply :: Apply JSONParse
derive newtype instance jsonParseApplicative :: Applicative JSONParse
derive newtype instance jsonParseFunctor :: Functor JSONParse
derive newtype instance jsonParseBind :: Bind JSONParse
derive newtype instance jsonParseMonad :: Monad JSONParse
derive newtype instance jsonParseTell :: MonadTell (Array Foreign.ForeignError) JSONParse
derive newtype instance jsonParseWriter :: MonadWriter (Array Foreign.ForeignError) JSONParse
derive newtype instance jsonParseThrow :: MonadThrow (NonEmptyList ForeignError) JSONParse
generalize :: forall m a. Monad m => Identity a -> m a
generalize = unwrap >>> pure
-- type Except e = ExceptT e Identity
genExcept :: forall m e a. Monad m => ExceptT e Identity a -> ExceptT e m a
genExcept = unwrap >>> generalize >>> ExceptT
readJSON' :: forall a. JSON.ReadForeign a => String -> JSONParse a
readJSON' s = JSONParse $ genExcept $ (pure >>> JSONWithErr) <$> (JSON.readJSON' s) -}
The error here, which covers the entire definition of readJSON', is:
Could not match type
JSONWithErr t2
with type
a0
while trying to match type JSONParse t1
with type JSONParse a0
while checking that expression (apply JSONParse) ((apply genExcept) ((map (...)) (readJSON' s)))
has type JSONParse a0
in value declaration readJSON'
where a0 is a rigid type variable
bound at (line 0, column 0 - line 0, column 0)
t1 is an unknown type
t2 is an unknown type
I've tried to simplify it a bit, but I seem to run into issues with constraints; in the following simplification, the typed hole doesn't work because of an earlier error:
tmp :: forall a. JSON.ReadForeign a => String -> ExceptT (NonEmptyList ForeignError) JSONWithErr a
tmp s = ?help (JSON.readJSON' s)
And the error:
No type class instance was found for
Simple.JSON.ReadForeign t0
The instance head contains unknown type variables. Consider adding a type annotation.
while applying a function readJSON'
of type ReadForeign t0 => String -> ExceptT (NonEmptyList ForeignError) Identity t0
to argument s
while inferring the type of readJSON' s
in value declaration tmp
I think you just got tangled in your own cleverness.
Take a look at what type of argument genExcept expects: ExceptT e Identity a, but you can also tell that e ~ (NonEmptyList ForeignError), because the result of genExcept gets later wrapped in JSONParse
So the type of argument that genExcept expects, as instantiated in the body of readJSON', is ExceptT (NonEmptyList ForeignError) Identity a, for which there is a handy type alias - F.
So we can tell that it must be:
(pure >>> JSONWithErr) <$> (JSON.readJSON' s) :: F a
But look at the return type of JSON.readJSON':
readJSON' :: forall a. ReadForeign a => String -> F a
So JSON.readJSON' s is already of type F a
And then you're wrapping it in some pure, and then in JSONWithErr, so the whole expression becomes:
(pure >>> JSONWithErr) <$> (JSON.readJSON' s) :: F (JSONWithErr (b a))
for some b determined by pure.
Which is not what genExcept expects. So quite naturally you get an error.
From what I can assume about your ultimate intentions, it seems that you can pass JSON.readJSON' s directly to genExcept, and the types will work out:
readJSON' :: forall a. JSON.ReadForeign a => String -> JSONParse a
readJSON' s = JSONParse $ genExcept $ JSON.readJSON' s
Related
when I write a type that e.g. only accepts strings: let type t1 = string
I can do let name : t1 = "A", but not let age : t1 = 1
But when I want to have a generic type that accepts any data type I have to do this: let type 'a t2 = 'a So I can do both let name : t2 = "A", let age : t2 = 1.
But why do I have to write let type 'a t2 = 'a instead of let type t2 = 'a?
The form
let type t1 = string
is not syntactically valid OCaml.
I imagine that you meant:
type t1 = string
Similarly, with the type constructor t2 defined as
type 'a t2 = 'a
then
let x : t2 = "hi"
is a type error because t2 is not a type but a type constructor of arity one.
The closest valid definition would be:
let x: string t2 = "hi"
which is equivalent
let x: 'a t2 = "hi"
because the type variable 'a is equated to 'a = string when inferring the type of x. But 'a t2 is an abbreviation for 'a, thus the above is still the same as
let x : string = "hi"
At a higher level, there is no useful generic type that accepts any data¹. Indeed, if it existed such type would break the type system.
¹ There are an advanced feature (GADTs or record with polymorphic fields) that allows to either create black-hole types that can carry a data of any kind but forbids any use of the data or types without any values of this type. However, it is probably better to first familiarize yourself with the core part of the type system before exploring those area.
To add to #octachron's answer, you can think of a declaration like type 'a t = 'a as the declaration of a function over types: given a type 'a, t will produce a new type 'a t (which in this case is just 'a). So you need the 'a just like you would need to declare the parameters in a function.
This shouldn't be confused with the occurrences of 'a in an explicit typing form like expr : 'a t. In this case, OCaml thinks of 'a as a type variable, which may or may not denote polymorphism. If you actually wanted to have polymorphism as is the polymorphic type, you'd have to introduce a polymorphic type variable either with 'a. ... or with a type a:
let id : 'a. 'a -> 'a = fun x -> x
let id (type a) (x : a) : a = x
However, note that these forms are not quite equivalent, there are subtle differences, and they are in any case different (in general, not in this specific example) from let id : 'a -> 'a = fun x -> x or let id (x : 'a) : 'a = x.
I have the following simple functions:
type t = unit -> unit
let f () = ()
let g ?opt:_ () = ()
With these functions defined, (f :> t) succeeds, but (g :> t) fails with the following error:
Error: This expression cannot be coerced to type t = unit -> unit; it has type
?opt:'a -> unit -> unit
but is here used with type t = unit -> unit
It seems that the type of g should be a subtype of t. If so, why does this type coercion fail?
If I define
fun id x = x
Then naturally id has type 'a -> 'a
Of course, id 0 evaluates to 0, which makes perfect sense.
Since this makes perfect sense, I should be able to encapsulate it by a function:
fun applyToZero (f: 'a -> 'a) = f 0
With the hope that applyToZero will have type ('a -> 'a) -> int and applyToZero id will evaluate to 0
But when I try to define applyToZero as above, SML/NJ gives an odd error message which begins:
unexpected exception (bug?) in SML/NJ: Match [nonexhaustive match failure]
raised at: ../compiler/Elaborator/types/unify.sml:84.37
This almost looks like a bug in the compiler itself. Weird, but possible.
But PolyML doesn't like it either (though its error message is less odd):
> fun applyToZero (f: 'a -> 'a) = f 0;
poly: : error: Type error in function application.
Function: f : 'a -> 'a
Argument: 0 : int
Reason: Can't unify int to 'a (Cannot unify with explicit type variable)
Found near f 0
The following does work:
fun ignoreF (f: 'a -> 'a) = 1
with the inferred type ('a -> 'a) -> int. This shows that it isn't impossible to create a higher order function of this type.
Why doesn't SML accept my definition of applyToZero? Is there any workaround that will allow me to define it so that its type is ('a -> 'a) -> int?
Motivation: in my attempt to solve the puzzle in this question, I was able to define a function tofun of type int -> 'a -> 'a and another function fromfun with the desired property that fromfun (tofun n) = n for all integers n. However, the inferred type of my working fromfun is ('int -> 'int) -> 'int). All of my attempts to add type annotations so that SML will accept it as ('a -> 'a) -> int have failed. I don't want to show my definition of fromfun since the person that asked that question might still be working on that puzzle, but the definition of applyToZero triggers exactly the same error messages.
It can't be done in plain Hindley-Milner, like used by SML, because it does not support so-called higher-ranked or first-class polymorphism. The type annotation 'a -> 'a and the type ('a -> 'a) -> int do not mean what you think they do.
That becomes clearer if we make the binder for the type variable explicit.
fun ignoreF (f: 'a -> 'a) = 1
actually means
fun 'a ignoreF (f: 'a -> 'a) = 1
that is, 'a is a parameter to the whole function ignoreF, not to its argument f. Consequently, the type of the function is
ignoreF : ∀ 'a. (('a -> 'a) -> int)
Here, I make the binder for 'a explicit in the type as a universal quantifier. That's how you write such types in type theory, while SML keeps all quantifiers implicit in its syntax. Now the type you thought this had would be written
ignoreF : (∀ 'a. ('a -> 'a)) -> int
Note the difference: in the first version, the caller of ignoreF gets to choose how 'a is instantiated, hence it could be anything, and the function cannot assume its int (which is why applyToZero does not type-check). In the second type, the caller of the argument gets to choose, i.e., ignoreF.
But such a type is not supported by Hindley-Milner. It only supports so-called prenex polymorphism (or rank 0 polymorphism) where all the ∀ are on the outermost level -- which is why it can keep them implicit, since there is no ambiguity under this restriction. The problem with higher-ranked polymorphism is that type inference for it is undecidable.
So your applyToZero cannot have the type you want in SML. The only way to achieve something like it is by using the module system and its functors:
functor ApplyToZero (val f : 'a -> 'a) = struct val it = f 0 end
Btw, the error message you quote from SML/NJ cannot possibly be caused by the code you showed. You must have done something else.
If we use Hindley-Milner type inference algorithm on fun applyToZero f = f 0 we are going to get f : int -> 'a because of the term f 0.
Obviously, f is a function f : 'b -> 'a. We apply this function to 0, thus 'b = int. Hence, the explicit type annotation f : 'a -> 'a produces the error you observe.
By the way, SML/NJ v110.80 works fine on my machine and prints the following error message:
stdIn:2.39-2.42 Error: operator and operand don't agree [overload - user bound tyvar]
operator domain: 'a
operand: [int ty]
in expression:
f 0
I have a basic question about types in signatures. If I have two ocaml files like:
istr.ml
type t = int
let get_string f v = f v
and fstr.ml
type t = float
let get_string f v = f v
and a signature
stri.mli
module type STR =
sig
type t
val get_string: (t -> string) -> t -> string
end
What is the type t in the above signature? Is it polymorphic?
it is an abstract type that hides the implementation and prevents the user to use it directly. see module system or RWO
But I am not sure that your current examples will work as your code examples does not seem to be in modules.
Why can't I coerce record types in OCaml? Base types like int works fine.
Below is an example where I construct a base module M which I include in module A. M.t is type abbriviated in A. As long as M.t is int, I can do A.t' :> M.t. When I change it to {i : int}, the compiler says it's not a subtype. I'm guessing there is a very specific reason for this?
module M = struct
type t = {i : int}
let make () = {i = 10}
end
module A : sig
include module type of M
type t' = private t
val make : unit -> t'
end = struct
include M
type t' = t
end
In the toplevel:
(A.make() :> M.t);;
Error: Type A.t' is not a subtype of M.t
That's because A.t' has no relation to M.t, because include does not "preserve" equality, it just literally duplicates the module structure (or signature) and inlines it in place (as fresh types and values). So type M.t doesn't have any relation to A.t and therefore to A.t' (and different record types do not have structural subtyping defined like say objects or modules).
Obvious fix is type t' = private M.t.
UPDATE
It appears the above is not fully correct, because type t' = private M.t in signature and include M type t' = t in implemention do typecheck, so include M preserves the equality (otherwise it couldn't match the signature type t' = private M.t), unlike copypasting the contents of M in the place of include M. But this "obviously" doesn't hold for include module type of which creates fresh types..