ae8394e9390b5cef3acf2e0c019829ad38640851
[nit.git] / lib / deriving.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
9 # another product.
10
11 # Automatic derivable implementation of standard basic methods.
12 #
13 # This module introduce `Derivable` as the main interface to implement (or auto-implement) and
14 # provides additional mixin-interfaces with specific default behavior of standard basic methods based
15 # on the services of this interface.
16 #
17 # The name *deriving* is inspired from the deriving mechanism of Haskell.
18 #
19 # This module also introduce a new annotation `auto_derive`. See `Derivable` for details.
20 module deriving is
21 new_annotation auto_derive
22 end
23
24 # Interface of objects that expose some kind of internal representation in a very unreliable way.
25 #
26 # The point of this interface is to allow objects to give a basic representation of
27 # themselves within a simple key-value dictionary.
28 # The specific semantic of each key and value is let unspecified.
29 #
30 # Moreover the class annotation `auto_derive` will automatically implements the
31 # interface with the attributes locally defined in the class.
32 #
33 # ~~~
34 # class A
35 # auto_derive
36 # var an_int: Int
37 # var a_string: String
38 # end
39 #
40 # var a = new A(5, "five")
41 # var map = a.derive_to_map
42 # assert map.length == 2
43 # assert map["an_int"] == 5
44 # assert map["a_string"] == "five"
45 # ~~~
46 interface Derivable
47 # Returns a map that loosely represents the object `self`.
48 #
49 # Warning: by default the method returns an empty Map.
50 # It is done this way so that subclasses can just call `super` and add their own attributes.
51 #
52 # Forgetting to redefine `derive_to_map` will broke the expectation of the user of the class
53 # Since an empty map is not POLA.
54 #
55 # Note that the semantic of keys and values is let unspecified.
56 # Moreover, there is no specification nor mechanism to avoid key collision.
57 fun derive_to_map: Map[String, nullable Object]
58 do
59 return new HashMap[String, nullable Object]
60 end
61 end
62
63 # Implementation of `to_s` for `Derivable` objects.
64 #
65 # The implementation uses `to_s` for each value of `attributes_to_map`.
66 #
67 # ~~~
68 # class A
69 # auto_derive
70 # super DeriveToS
71 # var an_int: Int
72 # var a_string: String
73 # end
74 #
75 # var a = new A(5, "five")
76 # assert a.to_s == "an_int:5; a_string:five"
77 # ~~~
78 #
79 # Warning: the method may go in an infinite recursion if there is a circuit in
80 # the implementation of `to_s`.
81 interface DeriveToS
82 super Derivable
83 redef fun to_s do return derive_to_map.join("; ", ":")
84 end
85
86 # Implementation of `==` and `hash` for `Derivable` objects.
87 #
88 # The implementations just call `==` and `hash` on `derive_to_map`.
89 #
90 # ~~~
91 # class A
92 # auto_derive
93 # super DeriveEqual
94 # var an_int: Int
95 # var a_string: String
96 # end
97 #
98 # var a = new A(5, "five")
99 # var b = new A(5, "five")
100 # var c = new A(6, "six")
101 # assert a == b
102 # assert a.hash == b.hash
103 # assert a != c
104 # ~~~
105 #
106 # Warning: the method may go in an infinite recursion if there is a circuit in
107 # the implementation of `==` or `hash`.
108 interface DeriveEqual
109 super Derivable
110 redef fun ==(other) do
111 if not other isa Derivable then return false
112 return derive_to_map == other.derive_to_map
113 end
114 redef fun hash do
115 return derive_to_map.hash
116 end
117 end