-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eio.Resource and Type-Safety #774
Comments
Hey @mbarbin I think to address the type-safety issues with Eio.Resource and Obj.magic here, start by minimizing or eliminating Obj.magic whenever possible. This function bypasses OCaml’s strict type system, which can lead to unexpected runtime errors. |
Thanks @Arogbonlo for trying to help here, I think the use of the object module in this part of Eio is v. different to other places and necessary. The implementation could perhaps use a few comments because reading the code is pretty tricky (the API docs are good though). |
Thanks @Arogbonlo that is sound advice. In this instance I've struggled in my attempts to remove the conceptually equivalent use of @patricoferris I also don't think it's possible to remove it without (perhaps invasive) changes to the exposed API. It would be sad if too many ergonomic aspects are lost in the process. It's probably low priority given that cases like the one I shared only arise with invalid usage of the library. At any rate, I'll let you know if I find anything interesting. Thank you for looking into the issue. |
Thanks @mbarbin, that's a problem, indeed! Logically, a handler is a function like this:
Then the magic shouldn't be needed. The array was intended to make it more efficient and ergonomic, but looks like it needs some revising! Note that OCaml's object types solve this problem easily, so if you can use them without annoying people then that would be a better option. (to keep the variance annotations you might need to do some magic on |
I can offer to have a look if you'd like since I'm already working on similar ideas for provider. No worries if you have other plans though, whatever makes most sense to you and the project. Thanks @talex5! |
Please go ahead! |
I was inspired by that famous TV show and decided to phone a friend on that one. This is joint work with @v-gb. I propose to use this PR1 as a preview. Please let me know what you think. I'd be happy to create a PR for Footnotes
|
I started a draft following a similar approach. I counted 22
Details below: Simple formThe 17/22 are of the simple form: K : ('t, (module S with type t = 't), [> ty]) Resource.pi They are compatible with the approach, and can be rewritten as: module K : sig
val pi : ('t, (module S with type t = 't), [> ty]) Resource.pi
end = Resource.Pi.Create (struct type 't iface = (module S with type t = 't) end) Parametrized tagsThe 5 remaining ones use another pattern. Consider Network : ('t, (module NETWORK with type t = 't and type tag = 'tag), [> 'tag ty]) Resource.pi In this pattern, the signature has 2 types. Its second type is bound as a parameter of the last argument of I hadn't noticed this pattern before. I find it more tricky because the tags part of module type Trait = sig
type t
type a
val get_a : t -> a
end
type ('a, 'impl, 'tag) Eio.Resource.pi +=
| Trait :
( 't
, (module Trait with type t = 't and type a = 'a)
, [ `A of 'a ] )
Eio.Resource.pi
let get_a (type tag) (Eio.Resource.T (t, ops) : [> `A of tag ] Eio.Resource.t) =
let (module T) = Eio.Resource.get ops Trait in
T.get_a t
;;
module Int_trait = struct
type t = int
type a = string
let get_a = string_of_int
end
let h : (int, [> `A of int ]) Eio.Resource.handler =
Eio.Resource.handler [ H (Trait, (module Int_trait)) ]
;;
let%expect_test "crash" =
let (a : int) = get_a (T (42, h)) in
print_s [%sexp { is_int = (Obj.is_int (Obj.repr a) : bool) }];
[%expect {| ((is_int false)) |}]
;; It looks like a potentially more concerning problem than the previous counter-example. All the more reasons for revising? lib_eio/time.ml
type (_, _, _) Resource.pi +=
| Clock : ('t, (module CLOCK with type t = 't and type time = 'time), [> 'time clock_ty]) Resource.pi The type net & process
They are parametrized by I don't know yet how to handle that part, and currently left it for future work. I am hoping this will not undermine the whole approach, but I am not confident. |
Hmm, interesting. I'm wondering about another possibility: what if we require users to provide a function (as above, but returning an option), and convert it to an array in I haven't tried it, but I think that would ensure that the values are sufficiently polymorphic. It would mean that when defining a new API you'd have to list all the |
You may be ahead of me on this. I'm under the impression that the user provided pattern matching would only get you as far as the I am curious if you envision that this would make the compiler infer the type of the tags during handler creation. It's hard to know without trying, but my impression is that as long as the function to build a handler does not constrain the tags on its own, the API will still be subject to the sort of the counter-example number 2 (constructing a segfault by improperly casting the tags). Such as in the current api: val handler : 't binding list -> ('t, _) handler I wonder if there is a construct in the language that could allow a unification of the tags while the bindings are joined (such as in a list, or pattern matching). Would using a object type instead of polymorphic variant in the tag phantom parameter give more power/flexibility? The 2 remaining cases are essentially the moral equivalent of a parametrized OCaml class. If the constrain for the parameter cannot be guaranteed via the tags, maybe a possible direction is to add more parameters to what's currently exposed: val get : ('t, 'param, 'tags) handler -> ('t, 'param, 'impl, 'tags) pi -> 'impl By the way, could you point me to an example in Eio for net and process that builds a handler that is not |
A little update on this. I continued to spend some time on this PR but I have nothing to show for it. I did try to thread through that extra Another idea: I was reading through the thread about capabilities on discuss, and wondered what things would look like under a different approach. This combines two ideas:
Both these branches are very experimental, I just wanted to try and see if it was possible to get something to compile. I focused on just a few of the types to see what happens. CC @patricoferris from whom I read in the discuss thread:
and thought maybe something in that direction could at the same time, resolve the safety issue, and do some exploration along some of the pain points discussed in the thread. Very speculative / experimental things at this time, I have not much more to offer at this time. Thank you! |
Edit: Another tentative approach that keeps the polymorphic convenience usage at call site without casting, and built on ocaml object, hopefully staying reasonably close to the current Eio. This is very on-going and exploratory: https://github.com/mbarbin/eio/tree/object-with-modules-exploratory and currently doesn't fully build yet. Perhaps that could serve as additional material to discuss if you want to have a meeting about this with the eio-devs. Happy end-of-year and talk soon. |
Hello Eio team,
I've been re-using the pattern from
Eio.Resource
in the provider project. Recently, I've spent some time trying to make the use ofObj.magic
more robust in provider. You can see some of the discussions and efforts here.During this process, I've encountered cases of invalid uses that lead to unfortunate results. I'm sharing one such case that is also applicable to
Resource
:I'm not sure if it is possible to address this issue. I'd be interested in discussions with the Eio team on this.
Thank you for your time and for Eio!
The text was updated successfully, but these errors were encountered: