From ed217277509606f794258d9700a9e3721acffb22 Mon Sep 17 00:00:00 2001 From: Mathieu Barbin Date: Wed, 21 Feb 2024 14:45:13 +0100 Subject: [PATCH] Rename Class => Trait --- src/provider.ml | 142 ++++++++++++------------- src/provider.mli | 159 +++++++++++++++------------- test/interface/directory_reader.ml | 9 +- test/interface/directory_reader.mli | 12 +-- test/interface/file_reader.ml | 6 +- test/interface/file_reader.mli | 4 +- test/interface/float_printer.ml | 9 +- test/interface/float_printer.mli | 4 +- test/interface/int_printer.ml | 8 +- test/interface/int_printer.mli | 4 +- test/providers/eio_reader.ml | 12 +-- test/providers/num_printer.ml | 4 +- test/providers/num_printer.mli | 2 +- test/providers/unix_reader.ml | 6 +- test/test__cache.ml | 24 ++--- test/test__introspection.ml | 22 ++-- test/test__invalid_tags.ml | 4 +- test/test__lookup.ml | 44 ++++---- test/test__make_interface.ml | 58 +++++----- test/test__override.ml | 19 ++-- test/test__reader.ml | 2 +- 21 files changed, 275 insertions(+), 279 deletions(-) diff --git a/src/provider.ml b/src/provider.ml index 7b84e71..1d2281e 100644 --- a/src/provider.ml +++ b/src/provider.ml @@ -1,7 +1,7 @@ let phys_same t1 t2 = phys_equal (Stdlib.Obj.repr t1) (Stdlib.Obj.repr t2) -module Class_id = struct - type ('t, 'implementation, 'tag) t = .. +module Trait = struct + type ('t, 'module_type, 'tag) t = .. module Info = struct type t = Stdlib.Obj.Extension_constructor.t @@ -28,129 +28,117 @@ module Class_id = struct let compare_by_uid id1 id2 = Uid.compare (uid id1) (uid id2) let same (id1 : _ t) (id2 : _ t) = phys_same id1 id2 -end -module Class = struct - type _ t = - | T : - { class_id : ('t, 'implementation, _) Class_id.t - ; implementation : 'implementation - } - -> 't t + module Implementation = struct + type ('t, 'module_type, 'tag) trait = ('t, 'module_type, 'tag) t - let uid (T { class_id; implementation = _ }) = Class_id.uid class_id - let info (T { class_id; implementation = _ }) = Class_id.info class_id + type _ t = + | T : + { trait : ('t, 'module_type, _) trait + ; impl : 'module_type + } + -> 't t - let compare_by_uid (T { class_id = id1; _ }) (T { class_id = id2; _ }) = - Class_id.compare_by_uid id1 id2 - ;; + let uid (T { trait; impl = _ }) = uid trait + let info (T { trait; impl = _ }) = info trait + + let compare_by_uid (T { trait = id1; _ }) (T { trait = id2; _ }) = + compare_by_uid id1 id2 + ;; + end - let implement (type a i) ~(class_id : (a, i, _) Class_id.t) (implementation : i) : a t = - T { class_id; implementation } + let implement (type a i) (trait : (a, i, _) t) ~(impl : i) : a Implementation.t = + Implementation.T { trait; impl } ;; end module Interface = struct (* We sort the element by their extension_id in increasing order. Element.(0) is a cache of the most recently looked up method. *) - type ('t, -'tags) t = 't Class.t array - - let make (type a) (classes : a Class.t list) : (a, _) t = - let classes = - let table = Hashtbl.create (module Class_id.Uid) in - List.iter classes ~f:(fun class_ -> - Hashtbl.set table ~key:(Class_id.uid class_) ~data:class_); - Hashtbl.data table |> List.sort ~compare:Class.compare_by_uid + type ('t, -'tags) t = 't Trait.Implementation.t array + + let make (type a) (implementations : a Trait.Implementation.t list) : (a, _) t = + let implementations = + let table = Hashtbl.create (module Trait.Uid) in + List.iter implementations ~f:(fun implementation -> + Hashtbl.set table ~key:(Trait.uid implementation) ~data:implementation); + Hashtbl.data table |> List.sort ~compare:Trait.Implementation.compare_by_uid in - match classes with + match implementations with | [] -> [||] | hd :: _ -> - (* We initialize the cache arbitrarily with the left most class. *) - Array.of_list (hd :: classes) + (* We initialize the cache arbitrarily with the left most trait. *) + Array.of_list (hd :: implementations) ;; - let same_class_uids : type a tags1 tags2. (a, tags1) t -> (a, tags2) t -> bool = + let same_trait_uids : type a tags1 tags2. (a, tags1) t -> (a, tags2) t -> bool = fun t1 t2 -> (* We skip the cell 0 which contains the cache. *) if Array.length t1 <> Array.length t2 then false else - Array.for_alli t1 ~f:(fun i class_ -> - i = 0 || 0 = Class.compare_by_uid class_ t2.(i)) + Array.for_alli t1 ~f:(fun i implementation -> + i = 0 || 0 = Trait.Implementation.compare_by_uid implementation t2.(i)) ;; let is_empty t = Array.length t = 0 - let cache t = if Array.length t = 0 then None else Some (Class.uid t.(0)) + let cache t = if Array.length t = 0 then None else Some (Trait.Implementation.uid t.(0)) - let classes t = + let implementations t = match Array.to_list t with | [] -> [] | _ :: tl -> tl ;; - let extend t ~with_ = make (classes t @ with_) + let extend t ~with_ = make (implementations t @ with_) let rec binary_search : type a implementation tags b. (a, tags) t - -> class_id:(a, implementation, tags) Class_id.t + -> trait:(a, implementation, tags) Trait.t -> update_cache:bool - -> if_not_found:(class_info:Class_id.Info.t -> b) + -> if_not_found:(trait_info:Trait.Info.t -> b) -> if_found:(implementation -> b) -> from:int -> to_:int -> b = - fun t ~class_id ~update_cache ~if_not_found ~if_found ~from ~to_ -> + fun t ~trait ~update_cache ~if_not_found ~if_found ~from ~to_ -> if from > to_ - then if_not_found ~class_info:(Class_id.info class_id) + then if_not_found ~trait_info:(Trait.info trait) else ( let mid = (from + to_) / 2 in - let (Class.T { class_id = elt; implementation } as class_) = t.(mid) in - match Class_id.compare_by_uid elt class_id |> Ordering.of_int with + let (Trait.Implementation.T { trait = elt; impl } as implementation) = t.(mid) in + match Trait.compare_by_uid elt trait |> Ordering.of_int with | Equal -> - if update_cache then t.(0) <- class_; - if_found (Stdlib.Obj.magic implementation) + if update_cache then t.(0) <- implementation; + if_found (Stdlib.Obj.magic impl) | Less -> - binary_search - t - ~class_id - ~update_cache - ~if_not_found - ~if_found - ~from:(mid + 1) - ~to_ + binary_search t ~trait ~update_cache ~if_not_found ~if_found ~from:(mid + 1) ~to_ | Greater -> - binary_search - t - ~class_id - ~update_cache - ~if_not_found - ~if_found - ~from - ~to_:(mid - 1)) + binary_search t ~trait ~update_cache ~if_not_found ~if_found ~from ~to_:(mid - 1)) ;; let make_lookup : type a implementation tags b. (a, tags) t - -> class_id:(a, implementation, tags) Class_id.t + -> trait:(a, implementation, tags) Trait.t -> update_cache:bool - -> if_not_found:(class_info:Class_id.Info.t -> b) + -> if_not_found:(trait_info:Trait.Info.t -> b) -> if_found:(implementation -> b) -> b = - fun t ~class_id ~update_cache ~if_not_found ~if_found -> + fun t ~trait ~update_cache ~if_not_found ~if_found -> if Array.length t = 0 - then if_not_found ~class_info:(Class_id.info class_id) + then if_not_found ~trait_info:(Trait.info trait) else ( - let (Class.T { class_id = cached_id; implementation }) = t.(0) in - if Class_id.same class_id cached_id - then if_found (Stdlib.Obj.magic implementation) + let (Trait.Implementation.T { trait = cached_id; impl }) = t.(0) in + if Trait.same trait cached_id + then if_found (Stdlib.Obj.magic impl) else binary_search t - ~class_id + ~trait ~update_cache ~if_not_found ~if_found @@ -159,38 +147,38 @@ module Interface = struct ;; module If_not_found = struct - let raise ~class_info = - raise_s [%sexp "Class not implemented", { class_info : Class_id.Info.t }] + let raise ~trait_info = + raise_s [%sexp "Trait not implemented", { trait_info : Trait.Info.t }] ;; - let none ~class_info:_ = None - let false_ ~class_info:_ = false + let none ~trait_info:_ = None + let false_ ~trait_info:_ = false end - let lookup t ~class_id = + let lookup t ~trait = make_lookup t - ~class_id + ~trait ~update_cache:true ~if_not_found:If_not_found.raise ~if_found:Fn.id ;; - let lookup_opt t ~class_id = + let lookup_opt t ~trait = make_lookup t - ~class_id + ~trait ~update_cache:true ~if_not_found:If_not_found.none ~if_found:Option.return ;; - let implements t ~class_id = - (* Only checking for the availability of the class doesn't affect the cache, + let implements t ~trait = + (* Only checking for the availability of the trait doesn't affect the cache, we leave it untouched in this case. *) make_lookup t - ~class_id + ~trait ~update_cache:false ~if_not_found:If_not_found.false_ ~if_found:(Fn.const true) diff --git a/src/provider.mli b/src/provider.mli index 000b753..65049ab 100644 --- a/src/provider.mli +++ b/src/provider.mli @@ -1,45 +1,49 @@ (** Parametrize your OCaml library with values that behave like objects but aren't. - A "provider" is a construct that implements a set of methods that an + A "provider" is a construct that implements a set of functionality that an library typically needs in order to provide certain functionality to a client. The module is divided into several submodules: - - {!module:Class_id}: To identify classes. - - {!module:Class}: For implementing classes. - - {!module:Interface}: Manages the set of classes that an object implements. + - {!module:Trait}: To identify and implement functionality. + - {!module:Interface}: Manages the set of traits that a provider implements. - {!module:Private}: Used for testing purposes. This module is inspired by the [Eio.Resource] module and provides a way to parameterize code when a library either doesn't want to or can't commit to a specific implementation. *) -module Class_id : sig - (** Identifying a class within the provider system. +module Trait : sig + (** Think of a trait as a way to identify and implement the signature of a + module that contains enough functions to support some functionality. The + type {!type:t} allows to identify a trait within the provider system. + The name was inspired from the Rust programming language construct of + the same name. - ['t] is the internal state of the provider itself. - - ['implementation] is the API that can be requested. - - ['tag] is the tag (or tags) indicating the supported class. It's a + - ['module_type] is the signature of a module implementing the trait. + - ['tag] is the tag (or tags) indicating the supported trait. It's a phantom type designed to make {!val:Interface.lookup} more type-safe. + This relates to Trait bounds in Rust. - The API requested is expected to be a module type (Eio supports single + ['module_type] is expected to be a module type (Eio supports single functions but this is discouraged through the use of this library). *) - type ('t, 'implementation, 'tag) t = .. + type ('t, 'module_type, 'tag) t = .. (** {1 Dump & debug} *) module Info : sig (** This type is primarily used for debugging purposes. - An [t] value includes the name of the class constructor and the module + An [t] value includes the name of the trait constructor and the module path where it was defined. It may also include the runtime id for the - extensible variant of the class id, but this is not included by default - as its value can be brittle (it may depend on the order in which modules + extensible variant of the trait, but this is not included by default as + its value can be brittle (it may depend on the order in which modules are evaluated). This type provides a way to retrieve and display detailed information - about a class, which can be useful for debugging and understanding the + about a trait, which can be useful for debugging and understanding the structure and behavior of the provider system. *) type t [@@deriving sexp_of] @@ -56,10 +60,10 @@ module Class_id : sig module Uid : sig (** A uid is particularly useful when you need to quickly look up or sort - classes, as it provides a consistent and unique way to identify each - class. You can use it to manipulate classes within container - structures, making it easier to store, retrieve, and compare classes - at runtime. *) + traits, as it provides a consistent and unique way to identify each + trait. You can use it to manipulate traits within container + structures, making it easier to store, retrieve, and compare traits at + runtime. *) type t [@@deriving compare, equal, hash, sexp_of] include Comparable.S with type t := t @@ -67,44 +71,47 @@ module Class_id : sig val uid : _ t -> Uid.t val same : _ t -> _ t -> bool -end -module Class : sig - (** This module is used by providers to implement classes. *) + module Implementation : sig + (** Representing an implementation for a trait. *) - type _ t = private - | T : - { class_id : ('t, 'implementation, _) Class_id.t - ; implementation : 'implementation - } - -> 't t + type ('t, 'module_type, 'tag) trait := ('t, 'module_type, 'tag) t - (** [implement ~class_id (module Impl)] returns a class that uses [Impl] as - the implementation for [class_id]. + type _ t = private + | T : + { trait : ('t, 'module_type, _) trait + ; impl : 'module_type + } + -> 't t - The tags associated with the [class_id] are ignored at this stage. The - handling of the tags happens at the interface building stage, not at the - granularity of each class. This means that the [implement] function - focuses solely on creating the class, without considering the tags that - indicate which classes are supported by the provider. *) - val implement : class_id:('t, 'implementation, _) Class_id.t -> 'implementation -> 't t + (** {1 Dump & debug} *) - (** {1 Dump & debug} *) + val uid : _ t -> Uid.t + val info : _ t -> Info.t + end + + (** [implement trait ~impl:(module Impl)] says to implement [trait] with + [Impl]. The module [Impl] provided must have the right module type as + specified by the type of [trait]. - val uid : _ t -> Class_id.Uid.t - val info : _ t -> Class_id.Info.t + The tags associated with the [trait] are ignored at this stage. The + handling of the tags happens at the interface building stage, not at the + granularity of each trait. This means that the {!val:implement} function + focuses solely on creating the implementation, without considering the + tags that indicate which traits are supported by the provider. *) + val implement : ('t, 'module_type, _) t -> impl:'module_type -> 't Implementation.t end module Interface : sig - (** Manipulating the set of classes implemented by a provider. + (** Manipulating the set of traits implemented by a provider. This module provides functions for creating an interface, as well as - retrieving and extending the classes implemented by an interface, making - it easy to manage the functionalities that a provider supports. *) + retrieving and extending the traits implemented by an interface, making it + easy to manage the functionalities that a provider supports. *) - (** An interface is essentially a collection of classes that an object - implements, each of which providing a specific set of functionalities - (one class = one first-class module with type t = 't). + (** An interface is essentially a collection of traits that a provider + implements, each of which providing a specific functionality (one trait + implementation = one first-class module with type t = 't). - ['t] is the internal state of the provider. - ['tags] indicate which functionality are supported by the provider. It @@ -131,36 +138,37 @@ module Interface : sig (** {1 Building interfaces} *) - (** [make classes] create a new interface from a list of classes. It only - keeps the last occurrence of each class. This means that the resulting - interface will not contain any duplicate classes, and the order of the - classes in the input list can affect its contents. *) - val make : 't Class.t list -> ('t, _) t + (** [make implementations] create a new interface from a list of + implementation. It only keeps the last implementation supplied for each + trait. This means that the resulting interface will not contain any + duplicate traits, and the order of the implementations in the input list + can affect its contents. *) + val make : 't Trait.Implementation.t list -> ('t, _) t - (** [classes t] returns a list of classes that the interface [t] - implements. See also {!extend}. *) - val classes : ('t, _) t -> 't Class.t list + (** [implementations t] returns a list of trait implementations that the + interface [t] supports. See also {!extend}. *) + val implementations : ('t, _) t -> 't Trait.Implementation.t list (** [extend t ~with_] extends the interface [t] and returns a new interface - that includes both the original and additional classes. The resulting - interface only contains the last occurrence of each class identifier, + that includes both the original and additional implementations. The + resulting interface only contains the last occurrence of each trait, prioritizing the rightmost elements in the combined list - [classes t @ with_]. *) - val extend : ('t, _) t -> with_:'t Class.t list -> ('t, _) t + [implementations t @ with_]. *) + val extend : ('t, _) t -> with_:'t Trait.Implementation.t list -> ('t, _) t (** {1 Lookup} A lookup operation is used to retrieve the implementation of a specific - class within an interface based on its class identifier. *) + trait within an interface. *) - (** [is_empty t] checks if an interface [t] implements any classes. An empty + (** [is_empty t] checks if an interface [t] implements any traits. An empty interface may be created using [make []]. It will cause any lookup operation to fail. It can be useful for initializing data structures or providing a base case for algorithms that process interfaces. *) val is_empty : ('t, _) t -> bool - (** [lookup t ~class_id] retrieves the implementation for a given class from - an interface. + (** [lookup t ~trait] retrieves the implementation for a given [trait] from an + interface. If the provider has correctly exported their implementation using the appropriate tags, the compiler will ensure that this function does not @@ -168,11 +176,11 @@ module Interface : sig programming error in the provider's setup). *) val lookup : ('t, 'tags) t - -> class_id:('t, 'implementation, 'tags) Class_id.t + -> trait:('t, 'implementation, 'tags) Trait.t -> 'implementation - (** [lookup_opt t ~class_id] returns the implementation of the class - ([Some implementation]) or indicates that the class is not implemented + (** [lookup_opt t ~trait] returns the implementation of the [trait] + ([Some implementation]) or indicates that the trait is not implemented ([None]). This is particularly useful in scenarios where a part of a program needs @@ -180,17 +188,16 @@ module Interface : sig available or not. *) val lookup_opt : ('t, _) t - -> class_id:('t, 'implementation, _) Class_id.t + -> trait:('t, 'implementation, _) Trait.t -> 'implementation option - (** [implements t ~class_id] says wether an interface implements a class. This - is [true] iif [lookup_opt t ~class_id] returns [Some _]. *) - val implements : ('t, _) t -> class_id:('t, _, _) Class_id.t -> bool + (** [implements t ~trait] says wether an interface implements a trait. This + is [true] iif [lookup_opt t ~trait] returns [Some _]. *) + val implements : ('t, _) t -> trait:('t, _, _) Trait.t -> bool end -(** A provider is a pair of a value and an interface for it. Think about [t] as - the internal state of an object, and [interface] as the set of methods - that the object implements. *) +(** A provider is a pair of a value and a set of traits that the provider + implements. *) type -'tags t = | T : { t : 't @@ -205,14 +212,14 @@ module Private : sig version of the library to be minted. Use at your own risk. *) module Interface : sig - (** [same_class_uids i1 i2] checks if the class identifiers of two - interfaces are the same and in the same order. *) - val same_class_uids : ('t, _) Interface.t -> ('t, _) Interface.t -> bool + (** [same_trait_uids i1 i2] checks if the traits of two interfaces are the + same and in the same order. *) + val same_trait_uids : ('t, _) Interface.t -> ('t, _) Interface.t -> bool (** Exported to test the caching strategy. Retains the most recently looked - up class. Currently returns [None] for empty interface, and if the - interface is not empty, returns the most recently looked up class + up trait. Currently returns [None] for empty interface, and if the + interface is not empty, returns the most recently looked up trait ([Some uid]) or an arbitrary initial value. *) - val cache : _ Interface.t -> Class_id.Uid.t option + val cache : _ Interface.t -> Trait.Uid.t option end end diff --git a/test/interface/directory_reader.ml b/test/interface/directory_reader.ml index 48d87f0..5482518 100644 --- a/test/interface/directory_reader.ml +++ b/test/interface/directory_reader.ml @@ -8,18 +8,17 @@ module Provider_interface = struct val readdir : t -> path:string -> string list end - type (_, _, _) Provider.Class_id.t += - | Directory_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | Directory_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t let make (type t) (module M : S with type t = t) = - Provider.Interface.make - [ Provider.Class.implement ~class_id:Directory_reader (module M) ] + Provider.Interface.make [ Provider.Trait.implement Directory_reader ~impl:(module M) ] ;; end let readdir (Provider.T { t; interface }) ~path = let module M = - (val Provider.Interface.lookup interface ~class_id:Provider_interface.Directory_reader) + (val Provider.Interface.lookup interface ~trait:Provider_interface.Directory_reader) in M.readdir t ~path ;; diff --git a/test/interface/directory_reader.mli b/test/interface/directory_reader.mli index fcfec61..9a9d892 100644 --- a/test/interface/directory_reader.mli +++ b/test/interface/directory_reader.mli @@ -43,10 +43,10 @@ module Provider_interface : sig [Directory_reader] below. *) val make : (module S with type t = 't) -> ('t, tag) Provider.Interface.t - (** The actual class id may or may not be exported by the provider interface - - either way works. That's left as a programmer's preference depending on - the context. When this constructor is exported, you can use it in - conjunction with {!Provider.Class.implement}. *) - type (_, _, _) Provider.Class_id.t += - | Directory_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + (** The actual trait constructor may or may not be exported by the provider + interface -- either way works. That's left as a programmer's preference + depending on the context. When this constructor is exported, you can use + it in conjunction with {!Provider.Trait.implement}. *) + type (_, _, _) Provider.Trait.t += + | Directory_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t end diff --git a/test/interface/file_reader.ml b/test/interface/file_reader.ml index 9fd10eb..b10b898 100644 --- a/test/interface/file_reader.ml +++ b/test/interface/file_reader.ml @@ -8,13 +8,13 @@ module Provider_interface = struct val load : t -> path:string -> string end - type (_, _, _) Provider.Class_id.t += - | File_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | File_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t end let load (Provider.T { t; interface }) ~path = let module M = - (val Provider.Interface.lookup interface ~class_id:Provider_interface.File_reader) + (val Provider.Interface.lookup interface ~trait:Provider_interface.File_reader) in M.load t ~path ;; diff --git a/test/interface/file_reader.mli b/test/interface/file_reader.mli index 1e939c3..7bc8d9a 100644 --- a/test/interface/file_reader.mli +++ b/test/interface/file_reader.mli @@ -15,6 +15,6 @@ module Provider_interface : sig val load : t -> path:string -> string end - type (_, _, _) Provider.Class_id.t += - | File_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | File_reader : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t end diff --git a/test/interface/float_printer.ml b/test/interface/float_printer.ml index 95b0fc8..bfbded0 100644 --- a/test/interface/float_printer.ml +++ b/test/interface/float_printer.ml @@ -8,18 +8,17 @@ module Provider_interface = struct val string_of_float : t -> float -> string end - type (_, _, _) Provider.Class_id.t += - | Float_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | Float_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t let make (type t) (module M : S with type t = t) = - Provider.Interface.make - [ Provider.Class.implement ~class_id:Float_printer (module M) ] + Provider.Interface.make [ Provider.Trait.implement Float_printer ~impl:(module M) ] ;; end let print (Provider.T { t; interface }) f = let module M = - (val Provider.Interface.lookup interface ~class_id:Provider_interface.Float_printer) + (val Provider.Interface.lookup interface ~trait:Provider_interface.Float_printer) in print_endline (M.string_of_float t f) ;; diff --git a/test/interface/float_printer.mli b/test/interface/float_printer.mli index 78f0549..99f9f04 100644 --- a/test/interface/float_printer.mli +++ b/test/interface/float_printer.mli @@ -17,6 +17,6 @@ module Provider_interface : sig val make : (module S with type t = 't) -> ('t, tag) Provider.Interface.t - type (_, _, _) Provider.Class_id.t += - | Float_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | Float_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t end diff --git a/test/interface/int_printer.ml b/test/interface/int_printer.ml index 76d3cf8..27a269d 100644 --- a/test/interface/int_printer.ml +++ b/test/interface/int_printer.ml @@ -8,17 +8,17 @@ module Provider_interface = struct val string_of_int : t -> int -> string end - type (_, _, _) Provider.Class_id.t += - | Int_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | Int_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t let make (type t) (module M : S with type t = t) = - Provider.Interface.make [ Provider.Class.implement ~class_id:Int_printer (module M) ] + Provider.Interface.make [ Provider.Trait.implement Int_printer ~impl:(module M) ] ;; end let print (Provider.T { t; interface }) i = let module M = - (val Provider.Interface.lookup interface ~class_id:Provider_interface.Int_printer) + (val Provider.Interface.lookup interface ~trait:Provider_interface.Int_printer) in print_endline (M.string_of_int t i) ;; diff --git a/test/interface/int_printer.mli b/test/interface/int_printer.mli index 83845f6..90d42eb 100644 --- a/test/interface/int_printer.mli +++ b/test/interface/int_printer.mli @@ -15,8 +15,8 @@ module Provider_interface : sig val string_of_int : t -> int -> string end - type (_, _, _) Provider.Class_id.t += - | Int_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Class_id.t + type (_, _, _) Provider.Trait.t += + | Int_printer : ('t, (module S with type t = 't), [> tag ]) Provider.Trait.t val make : (module S with type t = 't) -> ('t, tag) Provider.Interface.t end diff --git a/test/providers/eio_reader.ml b/test/providers/eio_reader.ml index b51d86e..fe74a1e 100644 --- a/test/providers/eio_reader.ml +++ b/test/providers/eio_reader.ml @@ -16,12 +16,12 @@ let make ~env : [ `Directory_reader | `File_reader ] Provider.t = { t = Impl.T { fs = Eio.Stdenv.fs env } ; interface = Provider.Interface.make - [ Provider.Class.implement - ~class_id:Interface.Directory_reader.Provider_interface.Directory_reader - (module Impl) - ; Provider.Class.implement - ~class_id:Interface.File_reader.Provider_interface.File_reader - (module Impl) + [ Provider.Trait.implement + Interface.Directory_reader.Provider_interface.Directory_reader + ~impl:(module Impl) + ; Provider.Trait.implement + Interface.File_reader.Provider_interface.File_reader + ~impl:(module Impl) ] } ;; diff --git a/test/providers/num_printer.ml b/test/providers/num_printer.ml index 8a9788b..ced04f3 100644 --- a/test/providers/num_printer.ml +++ b/test/providers/num_printer.ml @@ -11,9 +11,9 @@ let interface : (unit, [ `Int_printer | `Float_printer ]) Provider.Interface.t = Provider.Interface.make (List.concat [ Interface.Int_printer.Provider_interface.make (module Impl) - |> Provider.Interface.classes + |> Provider.Interface.implementations ; Interface.Float_printer.Provider_interface.make (module Impl) - |> Provider.Interface.classes + |> Provider.Interface.implementations ]) ;; diff --git a/test/providers/num_printer.mli b/test/providers/num_printer.mli index 70a1fc1..0925170 100644 --- a/test/providers/num_printer.mli +++ b/test/providers/num_printer.mli @@ -5,7 +5,7 @@ is not documented in details. Refer to {!Eio_reader} for more. *) (** In this case we decided to expose the type {!type:t} and {!val:interface}, - to demonstrate how to override a particular class of the provided + to demonstrate how to override a particular trait of the provided interface. See [test__override.ml]. *) type t = unit diff --git a/test/providers/unix_reader.ml b/test/providers/unix_reader.ml index 4913485..3990f47 100644 --- a/test/providers/unix_reader.ml +++ b/test/providers/unix_reader.ml @@ -13,9 +13,9 @@ let make () : [ `Directory_reader ] Provider.t = { t = () ; interface = Provider.Interface.make - [ Provider.Class.implement - ~class_id:Interface.Directory_reader.Provider_interface.Directory_reader - (module Impl) + [ Provider.Trait.implement + Interface.Directory_reader.Provider_interface.Directory_reader + ~impl:(module Impl) ] } ;; diff --git a/test/test__cache.ml b/test/test__cache.ml index a849a38..10cd128 100644 --- a/test/test__cache.ml +++ b/test/test__cache.ml @@ -1,5 +1,5 @@ -(* The class lookup implementation uses a cache to speed up the search when - looking up the same class over and over in a row. In this test we monitor how +(* The trait lookup implementation uses a cache to speed up the search when + looking up the same trait over and over in a row. In this test we monitor how the cache is updated. *) module Cache_state = struct @@ -15,14 +15,14 @@ end let%expect_test "override" = let num_printer = Providers.Num_printer.make () in let cache_state_of_uid uid = - if Provider.Class_id.Uid.equal + if Provider.Trait.Uid.equal uid - (Interface.Int_printer.Provider_interface.Int_printer |> Provider.Class_id.uid) + (Interface.Int_printer.Provider_interface.Int_printer |> Provider.Trait.uid) then Cache_state.Int_printer - else if Provider.Class_id.Uid.equal + else if Provider.Trait.Uid.equal uid (Interface.Float_printer.Provider_interface.Float_printer - |> Provider.Class_id.uid) + |> Provider.Trait.uid) then Cache_state.Float_printer else assert false [@coverage off] in @@ -45,7 +45,7 @@ let%expect_test "override" = ignore (Provider.Interface.lookup interface - ~class_id:Interface.Int_printer.Provider_interface.Int_printer + ~trait:Interface.Int_printer.Provider_interface.Int_printer : (module Interface.Int_printer.Provider_interface.S with type t = a))) interface; require_equal @@ -59,7 +59,7 @@ let%expect_test "override" = ignore (Provider.Interface.lookup interface - ~class_id:Interface.Float_printer.Provider_interface.Float_printer + ~trait:Interface.Float_printer.Provider_interface.Float_printer : (module Interface.Float_printer.Provider_interface.S with type t = a))) interface; require_equal @@ -74,7 +74,7 @@ let%expect_test "override" = (Option.is_some (Provider.Interface.lookup_opt interface - ~class_id:Interface.Int_printer.Provider_interface.Int_printer)); + ~trait:Interface.Int_printer.Provider_interface.Int_printer)); require_equal [%here] (module Cache_state) @@ -87,7 +87,7 @@ let%expect_test "override" = (Option.is_some (Provider.Interface.lookup_opt interface - ~class_id:Interface.Float_printer.Provider_interface.Float_printer)); + ~trait:Interface.Float_printer.Provider_interface.Float_printer)); require_equal [%here] (module Cache_state) @@ -100,7 +100,7 @@ let%expect_test "override" = [%here] (Provider.Interface.implements interface - ~class_id:Interface.Int_printer.Provider_interface.Int_printer); + ~trait:Interface.Int_printer.Provider_interface.Int_printer); let post_cache_state = cache_state interface in require_equal [%here] (module Cache_state) pre_cache_state post_cache_state in @@ -110,7 +110,7 @@ let%expect_test "override" = [%here] (Provider.Interface.implements interface - ~class_id:Interface.Float_printer.Provider_interface.Float_printer); + ~trait:Interface.Float_printer.Provider_interface.Float_printer); let post_cache_state = cache_state interface in require_equal [%here] (module Cache_state) pre_cache_state post_cache_state in diff --git a/test/test__introspection.ml b/test/test__introspection.ml index 71ae2b4..1450b7c 100644 --- a/test/test__introspection.ml +++ b/test/test__introspection.ml @@ -1,18 +1,18 @@ -(* This test demonstrates how to access information about the classes - implemented by a provider at runtime. This is a key aspect of introspection, - allowing you to understand the capabilities of a provider dynamically, as the - program is running. *) +(* This test demonstrates how to access information about the traits implemented + by a provider at runtime. This is a key aspect of introspection, allowing you + to understand the capabilities of a provider dynamically, as the program is + running. *) -let print_implemented_classes (Provider.T { t = _; interface }) = +let print_implemented_traits (Provider.T { t = _; interface }) = let info = - List.map (Provider.Interface.classes interface) ~f:(fun class_ -> - [%sexp (Provider.Class.info class_ : Provider.Class_id.Info.t)]) + List.map (Provider.Interface.implementations interface) ~f:(fun implementation -> + [%sexp (Provider.Trait.Implementation.info implementation : Provider.Trait.Info.t)]) in print_s [%sexp (info : Sexp.t list)] ;; let print_implements (Provider.T { t = _; interface }) = - let implements class_id = Provider.Interface.implements interface ~class_id in + let implements trait = Provider.Interface.implements interface ~trait in print_s [%sexp { implements = @@ -65,15 +65,15 @@ let%expect_test "introspection" = in Sexp.Atom (Int.to_string id) in - Ref.set_temporarily Provider.Class_id.Info.sexp_of_id sexp_of_id ~f:(fun () -> - print_implemented_classes unix_reader; + Ref.set_temporarily Provider.Trait.Info.sexp_of_id sexp_of_id ~f:(fun () -> + print_implemented_traits unix_reader; [%expect {| (( (id 0) (name Provider_test__Interface__Directory_reader.Provider_interface.Directory_reader))) |}]; - print_implemented_classes eio_reader; + print_implemented_traits eio_reader; [%expect {| (((id 0) diff --git a/test/test__invalid_tags.ml b/test/test__invalid_tags.ml index dac2250..93a01bf 100644 --- a/test/test__invalid_tags.ml +++ b/test/test__invalid_tags.ml @@ -18,8 +18,8 @@ let%expect_test "invalid tags" = require_does_raise [%here] (fun () -> print_42 (Invalid_int_printer.make ())); [%expect {| - ("Class not implemented" (( - class_info ( + ("Trait not implemented" (( + trait_info ( (id #id) (name Provider_test__Interface__Int_printer.Provider_interface.Int_printer))))) |}]; diff --git a/test/test__lookup.ml b/test/test__lookup.ml index 31f31f4..27a0c01 100644 --- a/test/test__lookup.ml +++ b/test/test__lookup.ml @@ -1,14 +1,14 @@ (* This test exercises the lookup mechanism of the provider library. It includes - a case with a sufficient number of classes (`A` to `F`) to ensure good test + a case with a sufficient number of traits (`A` to `F`) to ensure good test coverage of the various branches of the binary search lookup method. - Additionally, this test features an example where multiple classes implement + Additionally, this test features an example where multiple traits implement the same functionality (printing a tag) but with different behavior (each - class prints a different tag). This serves to demonstrate how a function of a - library (`print_tag`) can dynamically select the class based on the value of + trait prints a different tag). This serves to demonstrate how a function of a + library (`print_tag`) can dynamically select the trait based on the value of some parameter. - The testing of the lookup mechanism and the demonstration of dynamic class + The testing of the lookup mechanism and the demonstration of dynamic trait selection are orthogonal. They are combined in this test purely for testing purposes. *) @@ -34,16 +34,16 @@ end type 'a t = ([> Tag.t ] as 'a) Provider.t -type (_, _, _) Provider.Class_id.t += - | A : ('a, (module S with type t = 'a), [> `A ]) Provider.Class_id.t - | B : ('a, (module S with type t = 'a), [> `B ]) Provider.Class_id.t - | C : ('a, (module S with type t = 'a), [> `C ]) Provider.Class_id.t - | D : ('a, (module S with type t = 'a), [> `D ]) Provider.Class_id.t - | E : ('a, (module S with type t = 'a), [> `E ]) Provider.Class_id.t - | F : ('a, (module S with type t = 'a), [> `F ]) Provider.Class_id.t +type (_, _, _) Provider.Trait.t += + | A : ('a, (module S with type t = 'a), [> `A ]) Provider.Trait.t + | B : ('a, (module S with type t = 'a), [> `B ]) Provider.Trait.t + | C : ('a, (module S with type t = 'a), [> `C ]) Provider.Trait.t + | D : ('a, (module S with type t = 'a), [> `D ]) Provider.Trait.t + | E : ('a, (module S with type t = 'a), [> `E ]) Provider.Trait.t + | F : ('a, (module S with type t = 'a), [> `F ]) Provider.Trait.t module Selector = struct - type 'a t = T : ('a, (module S with type t = 'a), Tag.t) Provider.Class_id.t -> 'a t + type 'a t = T : ('a, (module S with type t = 'a), Tag.t) Provider.Trait.t -> 'a t let of_tag = function | `A -> T A @@ -57,8 +57,8 @@ end let print_tag (Provider.T { t; interface } : _ t) ~tag = match Selector.of_tag tag with - | T class_id -> - let module M = (val Provider.Interface.lookup interface ~class_id) in + | T trait -> + let module M = (val Provider.Interface.lookup interface ~trait) in M.print_tag t ;; @@ -100,12 +100,12 @@ end let provider () : _ t = let interface = Provider.Interface.make - [ Provider.Class.implement ~class_id:A (module Impls.A) - ; Provider.Class.implement ~class_id:B (module Impls.B) - ; Provider.Class.implement ~class_id:C (module Impls.C) - ; Provider.Class.implement ~class_id:D (module Impls.D) - ; Provider.Class.implement ~class_id:E (module Impls.E) - ; Provider.Class.implement ~class_id:F (module Impls.F) + [ Provider.Trait.implement A ~impl:(module Impls.A) + ; Provider.Trait.implement B ~impl:(module Impls.B) + ; Provider.Trait.implement C ~impl:(module Impls.C) + ; Provider.Trait.implement D ~impl:(module Impls.D) + ; Provider.Trait.implement E ~impl:(module Impls.E) + ; Provider.Trait.implement F ~impl:(module Impls.F) ] in Provider.T { t = (); interface } @@ -113,7 +113,7 @@ let provider () : _ t = let%expect_test "lookup" = let (Provider.T { t = _; interface } as t) = provider () in - print_s [%sexp (List.length (Provider.Interface.classes interface) : int)]; + print_s [%sexp (List.length (Provider.Interface.implementations interface) : int)]; [%expect {| 6 |}]; List.iter Tag.all ~f:(fun tag -> print_tag t ~tag); [%expect {| diff --git a/test/test__make_interface.ml b/test/test__make_interface.ml index 3d6ba48..93186a6 100644 --- a/test/test__make_interface.ml +++ b/test/test__make_interface.ml @@ -1,66 +1,68 @@ (* This test is focused on the "make interface" functionality. We are testing - that different ways to create an interface - using 'make', 'extend', 'class - implement', or the provider interface supplied maker - all result in + that different ways to create an interface -- using [make], [extend], [Trait. + implement], or the provider interface supplied maker -- all result in equivalent interfaces. This ensures consistency across different methods of interface creation. *) let%expect_test "make interface" = - let class1 = - Provider.Class.implement - ~class_id:Interface.Directory_reader.Provider_interface.Directory_reader - (module Providers.Eio_reader.Impl) + let trait1 = + Provider.Trait.implement + Interface.Directory_reader.Provider_interface.Directory_reader + ~impl:(module Providers.Eio_reader.Impl) in let eio1 = Interface.Directory_reader.Provider_interface.make (module Providers.Eio_reader.Impl) in - (match class1, List.hd_exn (Provider.Interface.classes eio1) with + (match trait1, List.hd_exn (Provider.Interface.implementations eio1) with | T t, T t' -> - require [%here] (Provider.Class_id.same t.class_id t'.class_id); + require [%here] (Provider.Trait.same t.trait t'.trait); [%expect {||}]; ()); - let class2 = - Provider.Class.implement - ~class_id:Interface.File_reader.Provider_interface.File_reader - (module Providers.Eio_reader.Impl) + let trait2 = + Provider.Trait.implement + Interface.File_reader.Provider_interface.File_reader + ~impl:(module Providers.Eio_reader.Impl) in - (match class1, class2 with + (match trait1, trait2 with | T t1, T t2 -> print_s [%sexp - { class1 = (Provider.Class_id.info t1.class_id : Provider.Class_id.Info.t) - ; class2 = (Provider.Class_id.info t2.class_id : Provider.Class_id.Info.t) + { trait1 = (Provider.Trait.info t1.trait : Provider.Trait.Info.t) + ; trait2 = (Provider.Trait.info t2.trait : Provider.Trait.Info.t) }]; [%expect {| - ((class1 ( + ((trait1 ( (id #id) (name Provider_test__Interface__Directory_reader.Provider_interface.Directory_reader))) - (class2 ( + (trait2 ( (id #id) (name Provider_test__Interface__File_reader.Provider_interface.File_reader)))) |}]; - require [%here] (not (Provider.Class_id.same t1.class_id t2.class_id)); + require [%here] (not (Provider.Trait.same t1.trait t2.trait)); [%expect {||}]; ()); - (match Provider.Interface.classes eio1 with + (match Provider.Interface.implementations eio1 with | [ c1 ] -> require_equal [%here] - (module Provider.Class_id.Uid) - (Provider.Class.uid c1) - (Provider.Class.uid class1); + (module Provider.Trait.Uid) + (Provider.Trait.Implementation.uid c1) + (Provider.Trait.Implementation.uid trait1); [%expect {||}] | _ -> assert false); let empty = Provider.Interface.make [] in require [%here] (Provider.Interface.is_empty empty); - require [%here] (List.is_empty (Provider.Interface.classes empty)); - let eio2 = Provider.Interface.make [ class2 ] in + require [%here] (List.is_empty (Provider.Interface.implementations empty)); + let eio2 = Provider.Interface.make [ trait2 ] in require [%here] (not (Provider.Interface.is_empty eio2)); - require [%here] (not (Provider.Private.Interface.same_class_uids empty eio2)); + require [%here] (not (Provider.Private.Interface.same_trait_uids empty eio2)); [%expect {||}]; - let eio3 = Provider.Interface.make [ class1; class2 ] in - let eio4 = Provider.Interface.extend eio1 ~with_:(Provider.Interface.classes eio2) in - require [%here] (Provider.Private.Interface.same_class_uids eio3 eio4); + let eio3 = Provider.Interface.make [ trait1; trait2 ] in + let eio4 = + Provider.Interface.extend eio1 ~with_:(Provider.Interface.implementations eio2) + in + require [%here] (Provider.Private.Interface.same_trait_uids eio3 eio4); [%expect {||}]; () ;; diff --git a/test/test__override.ml b/test/test__override.ml index c92ae08..10d15f9 100644 --- a/test/test__override.ml +++ b/test/test__override.ml @@ -1,4 +1,4 @@ -(* Here we verify that it is possible to override an existing class. *) +(* Here we verify that it is possible to override an existing trait. *) module Int_hum_printer = struct module Impl = struct @@ -14,19 +14,20 @@ module Int_hum_printer = struct Provider.Interface.extend Providers.Num_printer.interface ~with_: - [ Provider.Class.implement - ~class_id:Interface.Int_printer.Provider_interface.Int_printer - (module Impl) + [ Provider.Trait.implement + Interface.Int_printer.Provider_interface.Int_printer + ~impl:(module Impl) ] } ;; end let%expect_test "override" = - let print_implemented_classes (Provider.T { t = _; interface }) = + let print_implemented_traits (Provider.T { t = _; interface }) = let info = - List.map (Provider.Interface.classes interface) ~f:(fun class_ -> - [%sexp (Provider.Class.info class_ : Provider.Class_id.Info.t)]) + List.map (Provider.Interface.implementations interface) ~f:(fun implementation -> + [%sexp + (Provider.Trait.Implementation.info implementation : Provider.Trait.Info.t)]) in print_s [%sexp (info : Sexp.t list)] in @@ -35,7 +36,7 @@ let%expect_test "override" = Interface.Float_printer.print printer 1234.5678 in let num_printer = Providers.Num_printer.make () in - print_implemented_classes num_printer; + print_implemented_traits num_printer; [%expect {| (((id #id) @@ -48,7 +49,7 @@ let%expect_test "override" = 1234 1234.5678 |}]; let hum_printer = Int_hum_printer.make () in - print_implemented_classes hum_printer; + print_implemented_traits hum_printer; [%expect {| (((id #id) diff --git a/test/test__reader.ml b/test/test__reader.ml index 9cc986d..9ced45f 100644 --- a/test/test__reader.ml +++ b/test/test__reader.ml @@ -54,7 +54,7 @@ let print_all_text_files_with_lines_if_available t ~path = match Provider.Interface.lookup_opt interface - ~class_id:Interface.File_reader.Provider_interface.File_reader + ~trait:Interface.File_reader.Provider_interface.File_reader with | None -> "not-available" | Some (module File_reader) ->