* [Caml-list] Problem with universal functions in a module @ 2015-01-07 13:50 Goswin von Brederlow 2015-01-07 15:30 ` Goswin von Brederlow 2015-01-07 17:26 ` Jeremy Yallop 0 siblings, 2 replies; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-07 13:50 UTC (permalink / raw) To: caml users Hi, I have a functor that boxes a GADT type into an universal container. But then the problem is how to get the type back out again. Specifically I want to pass the data to a polymorphic function that doesn't care what type it gets since it will pattern match the GADT to determine the right type. But, because the argument is a GADT, ocaml complains after unboxing the type would escape its scope (see call_unboxed below). One can only call an universal function with the unboxed data and only records (and objects) can have universal functions. Problem is that I would like to keep the implementation of the helper record abstract and only provide a constructor (helper function below). But the constructor needs an universal function and function argument are only polymorphic (less general). A) why is a polymorphic function less general here? The print function is good enough for List.iter below or for constructing a helper but not indirectly via "M.helper fn". Why are function arguments less general than their original function? B) is there some way around this I'm not seeing? MfG Goswin -- ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-07 13:50 [Caml-list] Problem with universal functions in a module Goswin von Brederlow @ 2015-01-07 15:30 ` Goswin von Brederlow 2015-01-07 17:26 ` Jeremy Yallop 1 sibling, 0 replies; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-07 15:30 UTC (permalink / raw) To: caml users Somehow the example code got lost: module M = struct type _ t = Int : int -> int t | Float : float -> float t let print : type a . a t -> unit = function | Int i -> Printf.printf "%d\n" i | Float f -> Printf.printf "%f\n" f (* should be abstract *) type box = Box : 'a t -> box let box t = Box t (* let call_unboxed fn (Box t) = fn t Error: This expression has type a#0 t but an expression was expected of type a#0 t The type constructor a#0 would escape its scope *) (* should be abstract *) type 'a helper = { fn : 'b . 'b t -> 'a } (* let helper fn = { fn } Error: This field value has type 'c t -> 'd which is less general than 'b. 'b t -> 'a *) let call helper (Box t) = helper.fn t end open M let t = [box (Int 1); box (Float 2.0)] (* Works but is not possible if box is abstract *) let () = List.iter (function Box x -> print x) t (* Use conversion helper to call unboxed values with a function *) let h = { fn = print } let () = List.iter (call h) t MfG Goswin ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-07 13:50 [Caml-list] Problem with universal functions in a module Goswin von Brederlow 2015-01-07 15:30 ` Goswin von Brederlow @ 2015-01-07 17:26 ` Jeremy Yallop 2015-01-08 9:45 ` Ben Millwood 1 sibling, 1 reply; 11+ messages in thread From: Jeremy Yallop @ 2015-01-07 17:26 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml users On 7 January 2015 at 13:50, Goswin von Brederlow <goswin-v-b@web.de> wrote: > Why are function arguments less general than their original function? Polymorphic arguments complicate type checking and make type inference impossible, so OCaml doesn't allow them. > B) is there some way around this I'm not seeing? I don't think that there's any way of passing polymorphic arguments that works out simpler than using a record. Of course, in your simple example code it's possible to eliminate the GADT altogether, which would also eliminate the need for polymorphic arguments. Perhaps GADTs play a more essential role in your real code, though. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-07 17:26 ` Jeremy Yallop @ 2015-01-08 9:45 ` Ben Millwood 2015-01-08 15:21 ` Goswin von Brederlow 0 siblings, 1 reply; 11+ messages in thread From: Ben Millwood @ 2015-01-08 9:45 UTC (permalink / raw) To: Jeremy Yallop; +Cc: Goswin von Brederlow, caml users [-- Attachment #1: Type: text/plain, Size: 1592 bytes --] Parametric polymorphic values in OCaml's type system can be thought of as "choose an assignment of the variables in this type, and you can use this value with that monomorphic type". But there's no monomorphic type that lets 'helper' do the right thing, so you actually need something more expressive than ordinary polymorphic types. In Haskell there's higher-rank polymorphism, which in this case would look something like 'helper :: (forall a. T a -> b) -> Helper b', so that you could only pass sufficiently-polymorphic arguments to helper. However, as Jeremy said, there's a lot of added complexity involved. On 7 January 2015 at 17:26, Jeremy Yallop <yallop@gmail.com> wrote: > On 7 January 2015 at 13:50, Goswin von Brederlow <goswin-v-b@web.de> > wrote: > > Why are function arguments less general than their original function? > > Polymorphic arguments complicate type checking and make type inference > impossible, so OCaml doesn't allow them. > > > B) is there some way around this I'm not seeing? > > I don't think that there's any way of passing polymorphic arguments > that works out simpler than using a record. > > Of course, in your simple example code it's possible to eliminate the > GADT altogether, which would also eliminate the need for polymorphic > arguments. Perhaps GADTs play a more essential role in your real > code, though. > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > [-- Attachment #2: Type: text/html, Size: 2399 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-08 9:45 ` Ben Millwood @ 2015-01-08 15:21 ` Goswin von Brederlow 2015-01-08 16:25 ` Leo White 0 siblings, 1 reply; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-08 15:21 UTC (permalink / raw) To: caml-list On Thu, Jan 08, 2015 at 09:45:47AM +0000, Ben Millwood wrote: > Parametric polymorphic values in OCaml's type system can be thought of as > "choose an assignment of the variables in this type, and you can use this > value with that monomorphic type". But there's no monomorphic type that > lets 'helper' do the right thing, so you actually need something more > expressive than ordinary polymorphic types. > > In Haskell there's higher-rank polymorphism, which in this case would look > something like 'helper :: (forall a. T a -> b) -> Helper b', so that you > could only pass sufficiently-polymorphic arguments to helper. However, as > Jeremy said, there's a lot of added complexity involved. In ocaml that is 'a . 'a -> 'b, or for GADTS: type a . a -> 'b. The problem is the former is only allowed in records and objects, not for function arguments and the later is only for annotating code and not for signatures. I don't see how there can be much added complexity involved. The higher-rank polymorphism is already allowed in records and objects so the type system knows how to deal with them. At least when they are annotated. I wouldn't need ocaml to infere those types. > On 7 January 2015 at 17:26, Jeremy Yallop <yallop@gmail.com> wrote: > > > On 7 January 2015 at 13:50, Goswin von Brederlow <goswin-v-b@web.de> > > wrote: > > > Why are function arguments less general than their original function? > > > > Polymorphic arguments complicate type checking and make type inference > > impossible, so OCaml doesn't allow them. > > > > > B) is there some way around this I'm not seeing? > > > > I don't think that there's any way of passing polymorphic arguments > > that works out simpler than using a record. > > > > Of course, in your simple example code it's possible to eliminate the > > GADT altogether, which would also eliminate the need for polymorphic > > arguments. Perhaps GADTs play a more essential role in your real > > code, though. The real use case has a GADT as input for a functor. The functor defines the boxing, an equality and unbox_with_equal operation and that helper record. Actually I have a few flavours of the same for different input types: type 'a t, type ('a, 'b) t and GADTs with detached witness: type 'a key + type 'a value and so on. I wish I could make a generic helper record with type ('b, 'c) helper = { fn : 'a 'b -> 'c } *sigh* MfG Goswin ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-08 15:21 ` Goswin von Brederlow @ 2015-01-08 16:25 ` Leo White 2015-01-09 1:02 ` Jacques Garrigue 2015-01-10 17:52 ` Goswin von Brederlow 0 siblings, 2 replies; 11+ messages in thread From: Leo White @ 2015-01-08 16:25 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml-list > I don't see how there can be much added complexity involved. The > higher-rank polymorphism is already allowed in records and objects so > the type system knows how to deal with them. At least when they are > annotated. I wouldn't need ocaml to infere those types. It is possible that higher-rank types would not add too much complexity to OCaml. However, there is an important difference between that and the higher-rank polymorphism provided by records and objects: whether to instantiate a type or not must be inferred. For example, compare this example using polymorphic methods: let f (x : < m: 'a. 'a -> 'a >) = let poly = if true then x else object method m : 'a. 'a -> 'a = id end in let mono = if true then x#m else (fun x -> x + 1) in (poly, mono) with this example using proper higher-rank types: let f (x : 'a. 'a -> 'a) = let poly = if true then x else id in let mono = if true then x else (fun x -> x + 1) in (poly, mono) Using objects with polymorphic methods we can trivially see that the first use of `x` is not instantiated whilst the second use of `x` is instantiated because the first uses the object itself whilst the second uses the method `m`. With higher-rank polymorphism, we must infer this information. I don't think this can be done in a principal way, without adding complexity to the type langauge (see MLF), so there would need to be some additional type annotations. Regards, Leo ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-08 16:25 ` Leo White @ 2015-01-09 1:02 ` Jacques Garrigue 2015-01-10 18:02 ` Goswin von Brederlow 2015-01-10 17:52 ` Goswin von Brederlow 1 sibling, 1 reply; 11+ messages in thread From: Jacques Garrigue @ 2015-01-09 1:02 UTC (permalink / raw) To: Leo P White; +Cc: Goswin von Brederlow, OCaml Mailing List On 2015/01/09 01:25, Leo White wrote: > >> I don't see how there can be much added complexity involved. The >> higher-rank polymorphism is already allowed in records and objects so >> the type system knows how to deal with them. At least when they are >> annotated. I wouldn't need ocaml to infere those types. > > It is possible that higher-rank types would not add too much complexity > to OCaml. > > However, there is an important difference between that and the > higher-rank polymorphism provided by records and objects: whether to > instantiate a type or not must be inferred. Actually the formal system for polymorphic methods use a notion of boxing/unboxing of first-class polymorphic values. It would be trivial to add it to ocaml's type system. There was not much demand originally, but with the introduction of GADTs it becomes more interesting. let f (x : ['a. 'a -> 'a]) = let poly = if true then x else (Poly (fun x -> x) : ['a. 'a -> 'a]) in let mono = let Poly x = x in if true then x else (fun x -> x + 1) in (poly, mono) Note also that first-class modules already provide another flexible approach to first-class polymorphism, and while you need to define a package type, equality of package types is structural since 4.02. module type T = sig val f : 'a -> 'a end let f (module X : T) = let poly : (module T) = if true then (module X) else (module struct let f x = x end) in let mono = if true then X.f else (fun x -> x + 1) in (poly, mono) As you can see here, the only slightly heavy part is the syntax for building modules. Jacques Garrigue ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-09 1:02 ` Jacques Garrigue @ 2015-01-10 18:02 ` Goswin von Brederlow 0 siblings, 0 replies; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-10 18:02 UTC (permalink / raw) To: Jacques Garrigue; +Cc: Leo P White, OCaml Mailing List On Fri, Jan 09, 2015 at 10:02:30AM +0900, Jacques Garrigue wrote: > On 2015/01/09 01:25, Leo White wrote: > > > >> I don't see how there can be much added complexity involved. The > >> higher-rank polymorphism is already allowed in records and objects so > >> the type system knows how to deal with them. At least when they are > >> annotated. I wouldn't need ocaml to infere those types. > > > > It is possible that higher-rank types would not add too much complexity > > to OCaml. > > > > However, there is an important difference between that and the > > higher-rank polymorphism provided by records and objects: whether to > > instantiate a type or not must be inferred. > > Actually the formal system for polymorphic methods use a notion of boxing/unboxing > of first-class polymorphic values. It would be trivial to add it to ocaml's type system. > There was not much demand originally, but with the introduction of GADTs it becomes > more interesting. > > let f (x : ['a. 'a -> 'a]) = > let poly = > if true then x > else (Poly (fun x -> x) : ['a. 'a -> 'a]) > in > let mono = > let Poly x = x in > if true then x > else (fun x -> x + 1) > in > (poly, mono) > > Note also that first-class modules already provide another flexible approach to first-class > polymorphism, and while you need to define a package type, equality of package > types is structural since 4.02. > > module type T = sig val f : 'a -> 'a end > > let f (module X : T) = > let poly : (module T) = > if true then (module X) > else (module struct let f x = x end) > in > let mono = > if true then X.f > else (fun x -> x + 1) > in > (poly, mono) > > As you can see here, the only slightly heavy part is the syntax for building modules. > > Jacques Garrigue Modules don't improve anything here. The problem is still that you can't hide the implementation of the boxing: # let make : ('a -> 'a) -> (module T) = function | fn -> (module struct let f = fn end);; Error: Signature mismatch: Modules do not match: sig val f : '_a -> '_a end is not included in T Values do not match: val f : '_a -> '_a is not included in val f : 'a -> 'a And you need different modules for different kind of types. Like 'a t, ('a, 'b) t, ... So I think modules are just as unflexible as objects and records here. MfG Goswin ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-08 16:25 ` Leo White 2015-01-09 1:02 ` Jacques Garrigue @ 2015-01-10 17:52 ` Goswin von Brederlow 2015-01-10 18:49 ` Leo White 1 sibling, 1 reply; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-10 17:52 UTC (permalink / raw) To: Leo White; +Cc: caml-list On Thu, Jan 08, 2015 at 04:25:49PM +0000, Leo White wrote: > > I don't see how there can be much added complexity involved. The > > higher-rank polymorphism is already allowed in records and objects so > > the type system knows how to deal with them. At least when they are > > annotated. I wouldn't need ocaml to infere those types. > > It is possible that higher-rank types would not add too much complexity > to OCaml. > > However, there is an important difference between that and the > higher-rank polymorphism provided by records and objects: whether to > instantiate a type or not must be inferred. For example, compare this > example using polymorphic methods: > > let f (x : < m: 'a. 'a -> 'a >) = > let poly = > if true then x > else object method m : 'a. 'a -> 'a = id end > in > let mono = > if true then x#m ^^^ 'a . 'a -> 'a > else (fun x -> x + 1) ^^^^^^^^^^^^^^ int -> int > in > (poly, mono) > > with this example using proper higher-rank types: > > let f (x : 'a. 'a -> 'a) = > let poly = > if true then x > else id > in > let mono = > if true then x ^ 'a . 'a -> 'a > else (fun x -> x + 1) ^^^^^^^^^^^^^^ int -> int > in > (poly, mono) > Using objects with polymorphic methods we can trivially see that the > first use of `x` is not instantiated whilst the second use of `x` is > instantiated because the first uses the object itself whilst the second > uses the method `m`. > > With higher-rank polymorphism, we must infer this information. I don't > think this can be done in a principal way, without adding complexity to > the type langauge (see MLF), so there would need to be some additional > type annotations. I don't see anything to infere there. Only thing I see is that in the case of mono the universal function has to be unified with a single type function. But 'a . 'a -> 'a should unify with int -> int resulting in int -> int without problems. I see no difference between the record and the plain case. The record only adds a boxing around the type. It doesn't make the type any easier to infer or unify for me. MfG Goswin ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-10 17:52 ` Goswin von Brederlow @ 2015-01-10 18:49 ` Leo White 2015-01-12 14:28 ` Goswin von Brederlow 0 siblings, 1 reply; 11+ messages in thread From: Leo White @ 2015-01-10 18:49 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml-list > I don't see anything to infere there. Only thing I see is that in the > case of mono the universal function has to be unified with a single > type function. But 'a . 'a -> 'a should unify with int -> int > resulting in int -> int without problems. 'a. 'a -> 'a cannot unify with int -> int. If it did then 'a. 'a -> 'a wouldn't be polymorphic: it could not be used as both int -> int and float -> float. Instead 'a. 'a -> 'a must be instantiated to 'b -> 'b which can then be unified with int -> int. It is the descision whether or not to perform this instantiation that needs to be infered. To understand the full range of issues associated with higher-rank and impredicative polymorphism I suggest reading the relevant literature (e.g. "MLF: raising ML to the power of system F", "Semi-explicit first-class polymorphism for ML", "Flexible types: robust type inference for first-class polymorphism", "Boxy types: inference for higher-rank types and impredicativity", ...). Regards, Leo ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Problem with universal functions in a module 2015-01-10 18:49 ` Leo White @ 2015-01-12 14:28 ` Goswin von Brederlow 0 siblings, 0 replies; 11+ messages in thread From: Goswin von Brederlow @ 2015-01-12 14:28 UTC (permalink / raw) To: caml-list On Sat, Jan 10, 2015 at 06:49:31PM +0000, Leo White wrote: > > I don't see anything to infere there. Only thing I see is that in the > > case of mono the universal function has to be unified with a single > > type function. But 'a . 'a -> 'a should unify with int -> int > > resulting in int -> int without problems. > > 'a. 'a -> 'a cannot unify with int -> int. If it did then 'a. 'a -> 'a > wouldn't be polymorphic: it could not be used as both int -> int and > float -> float. Instead 'a. 'a -> 'a must be instantiated to 'b -> 'b > which can then be unified with int -> int. It is the descision whether > or not to perform this instantiation that needs to be infered. > > To understand the full range of issues associated with higher-rank and > impredicative polymorphism I suggest reading the relevant literature > (e.g. "MLF: raising ML to the power of system F", "Semi-explicit > first-class polymorphism for ML", "Flexible types: robust type inference > for first-class polymorphism", "Boxy types: inference for higher-rank > types and impredicativity", ...). > > Regards, > > Leo I acknowledge that I'm weak on the theory here but from the example the rules seems to be pretty simple: When trying to unify a universal function with a polymorphic or monomorphic function first instantiate. As said I don't expect ocaml to infer what functions are universal. That can be annotated. So the question isn't if we can infer from the mono case wether the function is universal or monomorphic. The function must already be annotated as universal. Dropping the universality to allow unification with other types seems to be straight forward though. In laymans terms: A function 'a . 'a -> 'a can always be used where 'b -> 'b (or in this case int -> int) is expected. That doesn't change the type of the function itself though. MfG Goswin ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2015-01-12 14:28 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-01-07 13:50 [Caml-list] Problem with universal functions in a module Goswin von Brederlow 2015-01-07 15:30 ` Goswin von Brederlow 2015-01-07 17:26 ` Jeremy Yallop 2015-01-08 9:45 ` Ben Millwood 2015-01-08 15:21 ` Goswin von Brederlow 2015-01-08 16:25 ` Leo White 2015-01-09 1:02 ` Jacques Garrigue 2015-01-10 18:02 ` Goswin von Brederlow 2015-01-10 17:52 ` Goswin von Brederlow 2015-01-10 18:49 ` Leo White 2015-01-12 14:28 ` Goswin von Brederlow
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox