lib: detach ForAbuser from Collection
[nit.git] / lib / for_abuse.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 # Service management trough the `for` control structure.
12 #
13 # The module is a proof-of-concept to investigate the abuse of
14 # the `for` structure to implement various services.
15 #
16 # The idea is that, with a `for`, the service-provider can:
17 # * control the end of the service (thus can finalize things
18 # like releasing ressources)
19 # * communicate data with the user on each iteration; the used can
20 # also communicate results to the provider.
21 module for_abuse
22
23 # Encapsulation of service in a `for`-compatible interface.
24 #
25 # The service is not effectively started until the iterate method
26 # is called. Then, each step of the iteration is a step in the service.
27 #
28 # While, for a typing point of view, abusers are just classes with an
29 # iterator method, the point of this class is to tag services that return
30 # a ForAbuser object.
31 #
32 # Note that using having `ForAbuser` as a genuine subclass of `Collection`
33 # works but is not recommended since it may cause mental health issues.
34 interface ForAbuser[E]
35 # Starts and control the service
36 fun iterator: Iterator[E] is abstract
37
38 # Starts and contol the service (for `nitc`)
39 fun iterate
40 !each(e: E)
41 do
42 var i = iterator
43 while i.is_ok do
44 each(i.item)
45 i.next
46 end
47 end
48 end
49
50 # Abuser to read a file, see `file_open`
51 private class ReadFileForAbuser
52 super ForAbuser[IFStream]
53 var path: String
54 redef fun iterator do return new ReadFileForAbuserIterator(path)
55 end
56
57 # Abuser iterator to read a file, see `file_open`
58 private class ReadFileForAbuserIterator
59 super Iterator[IFStream]
60 redef var item: IFStream
61 redef var is_ok = true
62 init(path: String)
63 do
64 # start of service is to open the file, and return in
65 item = new IFStream.open(path)
66 end
67 redef fun next
68 do
69 # end of service is to close the file
70 # there is only one iteration
71 is_ok = false
72 item.close
73 end
74 end
75
76 ####
77
78 # A comparison query.
79 # The user is asked to compare `a` with `b` then set `res` accordingly.
80 #
81 # * if `a < b` then the user sets `res` to -1
82 # * if `a > b` then the user sets `res` to 1
83 # * if `a == b` then the user sets `res` to 0
84 #
85 # It is the responsibility of the user to implement a total order.
86 # ie. the implemented comparison must be asymmetric, transitive and total.
87 class CompareQuery[E]
88 # The first element to compare
89 var a: E
90 # The second element to compare
91 var b: E
92 # The result of the comparison (according to the user)
93 var res writable = 0
94 end
95
96 # Abuser for sorting array, see `sort_fa`
97 private class SortAbuser[E]
98 super ForAbuser[CompareQuery[E]]
99 var array: Array[E]
100 redef fun iterator do return new SortAbuserIterator[E](array)
101 end
102
103 # Abuser iterator for sorting array, see `sort_fa`
104 # Implements a sort by permutation
105 private class SortAbuserIterator[E]
106 super Iterator[CompareQuery[E]]
107 # The index of the big loop
108 var i: Int
109 # The index of the small loop
110 var j: Int
111 # The array to sort
112 var array: Array[E]
113 # The query used to communicate with the user.
114 # For ecological concerns, a unique CompareQuery is instatiated.
115 var query: nullable CompareQuery[E]
116 redef fun item do return query.as(not null)
117 init(array: Array[E])
118 do
119 self.array = array
120 # Initialize the algorithm, see `next` for the rest
121 i = 0
122 j = 0
123 if not is_ok then return
124 query = new CompareQuery[E](array[i], array[j])
125 end
126 redef fun is_ok do return i < array.length - 1
127 redef fun next
128 do
129 # Process the last query
130 if item.res > 0 then
131 var tmp = array[i]
132 array[i] = array[j]
133 array[j] = tmp
134 end
135 # Get the next iteration
136 j += 1
137 if j >= array.length then
138 # End of small loop
139 i += 1
140 j = i + 1
141 end
142 if not is_ok then return
143 # Prepare the next query
144 item.a = array[i]
145 item.b = array[j]
146 item.res = 0
147 end
148 end
149
150 redef class Array[E]
151 # Sort an array trough a `for` abuse.
152 # The user uses the provided query (item) to implements its own comparison
153 #
154 # var a = [1, 3, 2]
155 # for q in a do q.res = q.a <=> q.b
156 # assert print a == 123
157 #
158 # Implements a sort by permutation.
159 fun sort_fa: ForAbuser[CompareQuery[E]]
160 do
161 return new SortAbuser[E](self)
162 end
163 end
164
165 ####
166
167 # Open and read a file trough a `for` abuse.
168 # The abuse just ensures that the file is closed after the reading.
169 #
170 # for f in file_open("/etc/issue") do
171 # print f.read_line
172 # end # f is automatically closed here
173 fun file_open(path: String): ForAbuser[IFStream]
174 do
175 return new ReadFileForAbuser(path)
176 end