on master help sign in
more docs updates to reflect new runtime types
commitb5a555cc362c70693f275619d03f77b354c69a74 authorAgentM <agentm@themactionfaction.com> dateSat 2 Apr 2016, 00:56 (11 years ago) parent054da5a
3 files changed+100−39
docs/15_minute_tutorial.markdown+2 docs/new_datatypes.markdown+34−3 docs/on_null.markdown+64−36
docs/15_minute_tutorial.markdown+2
@@ -282,6 +282,8 @@ Now we have jumped back to the master branch and we can confirm that the "employ
282282
283283Of course, Project:M36 supports [aggregate operators](docs/tutd_tutorial.markdown#aggregate-queries), [value functions](docs/tutd_tutorial.markdown#extend), [database constraints](docs/tutd_tutorial.markdown#constraints) including candidate and foreign keys, and other operators not mentioned in this brief tutorial.
284284
285+While this tutorial only covered an in-memory database, Project:M36 also supports ACID-compliant filesystem persistence as well as [server-based operation](docs/server_mode.markdown).
286+
285287## Conclusion
286288
287289The TutorialD dialect used by Project:M36 is ideal for learning about the relational algebra because it is quick to write and grammatically unambiguous.
docs/new_datatypes.markdown+34−3
@@ -2,17 +2,48 @@
22
33## Introduction
44
5−The most common data types are built into Project:M36 directly. Such types include integers, text, byte strings, booleans and floats. It can often be handy to create one's own data types in a database and manipulate them as first-class values through the relational algebra.
5+The most common data types are built into Project:M36 directly. Such types include integers, text, byte strings, booleans and floats. It can often be handy to create one's own data types in a database and manipulate them as first-class values through the relational algebra.
6+
7+In addition, the Project:M36 runtime supports algebraic data types in the same vein as Haskell itself.
68
79Project:M36 makes it easy to add any Haskell data type to the database. Any type which includes instances for a set of typeclasses can be supported.
810
9−## Support
11+## Support for Runtime Types
12+
13+Use the ```tutd``` interpreter to create new type and data constructors with essentially the same syntax as in Haskell:
14+
15+```
16+TutorialD (master): data Hair = Bald | Brown | Blond | OtherColor Text
17+TutorialD (master): :showexpr relation{tuple{name "Steve", hair Blond}, tuple{name "Sam", hair OtherColor "Grey"}}
18+┌─────────────────┬──────────┐
19+│hair::Hair │name::Text│
20+├─────────────────┼──────────┤
21+│Blond │"Steve" │
22+│OtherColor "Grey"│"Sam" │
23+└─────────────────┴──────────┘
24+```
25+
26+SQL's enumerations or even joins do not compare favorably to a genuine algebraic data type. Exercise for the reader: why is this? Do you agree?
27+
28+Types can be forgotten as well:
29+
30+```
31+TutorialD (master): undata Hair
32+```
33+
34+These runtime types can therefore change between transactions. Note that the underlying data values do not change when the types change. Once changed, only new values will reflect the new data constructors.
35+
36+The downside to this approach is that functions which operate on these runtime types cannot be written in the TutorialD interpreter (yet).
37+
38+Note that the Haskell-standard "Maybe a" and "Either a b" data types are included by default.
39+
40+## Support for Haskell Types
1041
1142Any Haskell data type which is an instance of ```Atomable``` is a candidate for a Project:M36 value called an ```Atom```. Database tuples are composed of Atoms.
1243
1344Project:M36 does not yet support dynamically-loaded components, so the data type must be compiled into the Project:M36 backend.
1445
15−### Implementation
46+### Implementation for Haskell Types
1647
17481. Create an instance of your data type for the ```Atomable``` typeclass. The ```Atomable``` typeclass implies instances for a variety of standard typeclasses.
18491. Update the ```Binary Atom``` instance to include deserialization for your type.
docs/on_null.markdown+64−36
@@ -10,9 +10,9 @@ SQL intentionally differentiates NULL from all other values. NULL is not an inte
1010
1111```
1212postgres=# select NULL = NULL;
13− ?column?
13+ ?column?
1414----------
15−
15+
1616(1 row)
1717```
1818
@@ -68,7 +68,7 @@ postgres=# select * from x where a=a;
6868Example 3: Return all rows where b is from a list of NULL.
6969```
7070postgres=# select * from x where b = any(NULL);
71− a | b
71+ a | b
7272---+---
7373(0 rows)
7474```
@@ -101,7 +101,7 @@ postgres=# select * from x where exists (SELECT * FROM x as y where (a,b) = (y.a
101101(3 rows)
102102````
103103
104−All of the above query examples conform to the SQL standard with regards to NULL handling.
104+All of the above query examples conform to the SQL standard with regards to NULL handling.
105105
106106
107107## Inconsistencies Leading to Errors
@@ -110,8 +110,8 @@ If the difference between NULL handling in ```ANY``` versus ```EXISTS``` didn't
110110
111111Example 7: Aggregate a sum over a in x where one a value is NULL.
112112```
113−postgres=# select sum(a) from x;
114− sum
113+postgres=# select sum(a) from x;
114+ sum
115115-----
116116 3
117117(1 row)
@@ -119,20 +119,20 @@ postgres=# select sum(a) from x;
119119Example 8: Aggregate a sum over two NULLs.
120120```
121121postgres=# select sum(a) from (values (NULL::int),(NULL)) as y(a); --sum of only NULLs is NULL
122− sum
122+ sum
123123-----
124−
124+
125125(1 row)
126126```
127127Example 9: Count the number of a values in x. Count the number of rows in x.
128128```
129129postgres=# select count(a) from x;
130− count
130+ count
131131-------
132132 2
133133(1 row)
134134postgres=# select count(*) from x;
135− count
135+ count
136136-------
137137 3
138138(1 row)
@@ -142,17 +142,17 @@ Example 10: Aggregate the sum of an empty table.
142142postgres=# create table z(a int);
143143CREATE TABLE
144144postgres=# select sum(a) from z; -- sum of empty table is NULL
145− sum
145+ sum
146146-----
147−
147+
148148(1 row)
149149```
150150Example 11: NULL multipled by zero plus three.
151151```
152152postgres=# select 0*NULL + 3;
153− ?column?
153+ ?column?
154154----------
155−
155+
156156(1 row)
157157```
158158
@@ -161,9 +161,9 @@ This logic follows into arrays:
161161Example 12: Is NULL equal to any element in the array?
162162```
163163postgres=# select NULL = ANY(array[1,2,3,NULL]);
164− ?column?
164+ ?column?
165165----------
166−
166+
167167(1 row)
168168```
169169
@@ -172,13 +172,13 @@ and row-valued expressions:
172172Example 13: Is the (NULL,1) tuple equal to NULL? Is it not equal to NULL?
173173```
174174postgres=# select (NULL,1) is null;
175− ?column?
175+ ?column?
176176----------
177177 f
178178(1 row)
179179
180180postgres=# select (NULL,1) is not null;
181− ?column?
181+ ?column?
182182----------
183183 f
184184(1 row)
@@ -193,12 +193,12 @@ SQL does not enforce the relational model constraint that rows (tuples) be uniqu
193193Example 14: Create multiple NULLs in a result set.
194194```
195195postgres=# select * from (values (34),(50),(NULL),(NULL)) as employee_age(val);
196− val
196+ val
197197-----
198198 34
199199 50
200−
201−
200+
201+
202202(4 rows)
203203```
204204
@@ -222,14 +222,14 @@ postgres=# insert into z(a,b) values (2,'two'),(NULL,NULL) returning a,b;
222222 a | b
223223---+-----
224224 2 | two
225− |
225+ |
226226(2 rows)
227227postgres=# select x.a as xa,x.b as xb,z.a as za,z.b as zb from x left join z on x.a is not distinct from z.a;
228228 xa | xb | za | zb
229229----+-------+----+-----
230− 1 | one | |
230+ 1 | one | |
231231 2 | two | 2 | two
232− | three | |
232+ | three | |
233233(3 rows)
234234
235235```
@@ -306,7 +306,7 @@ and some examples values:
306306* ```PreciseAge 25```
307307* ```NotApplicable```
308308* ```ApproximateAge 30 40``` approximately between 30-40 years old
309−
309+
310310Already, we can see that this single data type can pack far more useful information than any combination of an integer and NULL.
311311
312312### Algebraic Data Types In SQL
@@ -317,8 +317,8 @@ An algebraic data type may be emulated in SQL, albeit in a convoluted fashion an
317317postgres=# create type age_type as enum ('precise_age', 'forgot_to_ask', 'refused_to_disclose', 'not_applicable', 'approximate_age');
318318CREATE TYPE
319319
320−postgres=# create table hospital_patient(name text not null,
321− age age_type not null,
320+postgres=# create table hospital_patient(name text not null,
321+ age age_type not null,
322322 precise_age int,
323323 approximate_age int[2]);
324324postgres=# insert into hospital_patient(name,age,precise_age,approximate_age) values ('Bob','approximate_age',NULL,array[30,40]);
@@ -342,6 +342,38 @@ But all we have really done is add even more NULLs. Retrieving a sensible value
342342
343343### Algebraic Data Types In Project:M36
344344
345+## Runtime Types
346+
347+Project:M36 supports management of new algebraic data types at runtime. These types are associated with transactions and can changed over time whereas primitive Atoms cannot be changed.
348+d
349+```
350+TutorialD (master): data Age = PreciseAge Int | ForgotToAsk | RefusedToDisclose | NotApplicable | ApproximateAge Int Int
351+TutorialD (master): :showexpr relation{tuple{name "Steve", age ApproximateAge 20 30}, tuple{name "Bob", age RefusedToDisclose}, tuple {name "Initech Corp.", age NotApplicable}}
352+┌────────────────────┬───────────────┐
353+│age::Age │name::Text │
354+├────────────────────┼───────────────┤
355+│ApproximateAge 20 30│"Steve" │
356+│NotApplicable │"Initech Corp."│
357+│RefusedToDisclose │"Bob" │
358+└────────────────────┴───────────────┘
359+```
360+
361+Note that the two values which would normally be rendered as NULL in SQL are indeed proper values and can be distinguished. Three-valued logic makes no appearance. The type constructors can also have arguments:
362+
363+```
364+TutorialD (master): data Tree a = Node a (Tree a) (Tree a) | EmptyNode
365+TutorialD (master): :showexpr relation{tuple{family "Stevenson", familytree Node "Jim" (Node "Cindy" EmptyNode EmptyNode) (Node "Mike" EmptyNode EmptyNode)}}
366+┌────────────┬───────────────────────────────────────────────────────────────────────────────┐
367+│family::Text│familytree::Tree (a::Text) │
368+├────────────┼───────────────────────────────────────────────────────────────────────────────┤
369+│"Stevenson" │Node "Jim" (Node "Cindy" EmptyNode EmptyNode) (Node "Mike" EmptyNode EmptyNode)│
370+└────────────┴───────────────────────────────────────────────────────────────────────────────┘
371+```
372+
373+Algebraic data types in SQL are effectively impossible.
374+
375+## Haskell Types
376+
345377Any Haskell data type can be turned into a value (called an "atom") in Project:M36. The following example illustrates:
346378
347379```haskell
@@ -365,7 +397,7 @@ data AgeType = PreciseAge Int |
365397 NotApplicable |
366398 ApproximateAge Int Int
367399 deriving (Eq,Show,Read,Hashable,Binary,Typeable,NFData,Generic)
368−
400+
369401instance Atomable AgeType
370402
371403failFastMaybe :: (Show a) => Maybe a -> IO ()
@@ -378,15 +410,15 @@ failFastEither (Right val) = return val
378410
379411ageAtomType :: AtomType
380412ageAtomType = atomTypeForProxy (Proxy :: Proxy AgeType)
381−
413+
382414runExample :: IO ()
383415runExample = do
384416 let bob_relation_attrs = attributesFromList [Attribute "name" stringAtomType,
385417 Attribute "age" ageAtomType]
386418 relvar_name = "hospital_patient"
387− age_value_in = ApproximateAge 30 40
388− bob_relation_err = mkRelationFromList
389− bob_relation_attrs
419+ age_value_in = ApproximateAge 30 40
420+ bob_relation_err = mkRelationFromList
421+ bob_relation_attrs
390422 [[Atom ("Bob"::Text), Atom age_value_in]]
391423
392424 connerr <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
@@ -427,7 +459,3 @@ Regardless, modeling the fact that some information may be missing is valuable,
427459Date, C.J. Darwen, Hugh. [Database Explorations: Essays on the Third Manifesto and Related Topics](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) chapters 23-29
428460
429461Date, C.J. [Database in Depth](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) pages 53-55
430−
431−
432−
433−