Skip to content
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

Deriving for types with a phantom type parameter #264

Open
brokenpylons opened this issue Jul 26, 2022 · 6 comments
Open

Deriving for types with a phantom type parameter #264

brokenpylons opened this issue Jul 26, 2022 · 6 comments

Comments

@brokenpylons
Copy link

brokenpylons commented Jul 26, 2022

Is there a way to derive: eq, ord, show, for types that have a phantom type parameter?
For example:

type 'a t = Thing

The equals function generated for this type would have a signature ('a -> 'a -> bool) -> 'a t -> 'a t -> bool, is there a way remove the first argument and generate just 'a t -> 'a t -> bool? The passed comparison function will never be used inside anyways.

@brokenpylons
Copy link
Author

brokenpylons commented Aug 8, 2022

I later realized you could do:

type phantom = P
[@@deriving eq, ord, show]

type 'a t = Thing
[@@deriving eq, ord, show]

type usage = phantom t
[@@deriving eq, ord, show]

This way, it fills in the dummy argument and it still works. However, this solution is unsatisfactory, because the rest of the code still needs to account for this extra argument, so I'm reopening.

@sim642
Copy link
Contributor

sim642 commented Aug 17, 2022

If t isn't recursive (and thus its derived equal isn't recursive), then you should be able to just do

type 'a t = Thing
[@@deriving eq]
let equal = equal (fun _ _ -> assert false) (* 'a doesn't occur in constructor args, so this function is never called *)

@brokenpylons
Copy link
Author

This doesn't work if you use this type inside another type and want to derive eq again.

type phantom = P
[@@deriving eq]

type 'a t = Thing
[@@deriving eq]

let equal x y = equal (fun _ _ -> false) x y

type usage = phantom t
[@@deriving eq]

This fails with This expression should not be a function, the expected type is 'a t.

type 'a t = Thing
[@@deriving eq]

let equal x y = equal (fun _ _ -> false) x y

type 'a usage = 'a t
[@@deriving eq]

This fails with This expression has type 'a -> 'a -> bool but an expression was expected of type 'b usage.

I guess you could combine this with my previous solution:

type phantom = P
[@@deriving eq]

type 'a t = Thing
[@@deriving eq]

let equal' x y = equal (fun _ _ -> false) x y

type 'a usage = 'a t
[@@deriving eq]

let equal_usage' x y = equal_usage (fun _ _ -> false) x y

Is this the best that can be done?

@kit-ty-kate
Copy link
Collaborator

type phantom = P
[@@deriving eq]

type 'a t = Thing
[@@deriving eq]

type 'a usage = 'a t
[@@deriving eq]

let equal = equal (fun _ _ -> assert false)
let equal_usage = equal_usage (fun _ _ -> assert false)

no need to define a new equal' function if equal is defined after the type definitions

@sim642
Copy link
Contributor

sim642 commented Aug 17, 2022

I suspect this reveals why such support wouldn't fully work. Even if for type 'a t = Thing we derive val equal: 'a t -> 'a t -> bool, then whenever t is used in another type that we want to derive for (e.g. in usage), it is instantiated with one type parameter and thus val equal: ('a -> 'a -> bool) -> 'a t -> 'a t -> bool is assumed to exist instead. Subsequent uses of t don't know that it's a phantom type parameter.

@brokenpylons
Copy link
Author

brokenpylons commented Aug 17, 2022

Couldn't you examine the type, to tell if the type parameters are phantom? I guess that wouldn't work if the type was abstract, though (maybe if you could somehow tag the parameter as phantom).

If the usage is in a separate module, I need to do something like the following, right?

module Thing = struct
  module Deriving = struct
    type phantom = P
    [@@deriving eq]

    type 'a t = Thing
    [@@deriving eq]
  end
  include Deriving
  let equal x y = equal (fun _ _ -> false) x y
end

module Usage = struct
  module Deriving = struct
    type 'a t = 'a Thing.Deriving.t
    [@@deriving eq]
  end
  include Deriving
  let equal x y = equal (fun _ _ -> false) x y
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants