Bj      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ BPresentation of tables in a two-dimensional fixed-width font form. Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalSafe( Given two lists of orderables, produces a list of equal length where each element is that which is the maximum of those elements that are in the same position in the two input lists.ZGets the maximum length of each column of a value consisting of a header and a single linePGets the width of the columns of a value in when presented in a columnar format._Gets the maximum length of each column of a value consisting of a header and zero or more lines8Builds a one-line representation of a value, plus header:Builds a multi-line representation of a value, plus headerTransposes a list of lists of lists, padding the lists of the second dimension with empty lists if they are shorter than the other rows.KGives the head of the argument, or the empty list if the argument is empty.KGives the tail of the argument, or the empty list if the argument is empty.HPresentation of HList values in a two-dimensional fixed-width font form. Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone (3457>LN@Type synoyms used when building the table header with type names Show a TypeRep!-Show a TypeRep, using the given type synonyms;hRecFNames $ sno .=. "S1" .*. status .=. 10 .*. emptyRecord["sno","status"]<hRecQFNames $ sno .=. "S1" .*. status .=. 10 .*. emptyRecord%["sno :: String","status :: Integer"]%/Prints a set of HList records in a table format&IPrints a set of HList records in a table format, with types in the header'jPrints a set of HList records in a table format, with types that use the given type synonyms in the headerB  !"#   $%&'  !  !"#$%&'! %&'$ !#" 4    !"#   $%&'  7Predicates and aggregates useful to relational querying Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gml dt cm experimentalSafe((Reverse predicate application. As "Data.Function.&", the reverse function application operator, but restricted to a boolean result.)plusminus 9 3 10True* 10 &= 9 3True/rPrint$ p "# (\[pun|weight|] -> weight &= 9 3))% %%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%)% pno % pName % color % weight % city %)%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a)% P1 % Nut % Red % 12 % 1 % London %)% P5 % Cam % Blue % 12 % 1 % Paris %)%%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%+ 10 &= 9 +- 3True,5 `between` (5,9)True- 5 &= 5 >=<= 9True. 5 &= 5 "e"d 9True2rPrint$ p "# (\[pun|weight|] -> weight &= 11 "e"d 14))% %%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%)% pno % pName % color % weight % city %)%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a)% P1 % Nut % Red % 12 % 1 % London %)% P4 % Screw % Red % 14 % 1 % London %)% P5 % Cam % Blue % 12 % 1 % Paris %)%%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%/Between exclusive5 `xBetween` (5,9)False0 5 &= 5 >< 9False2rPrint$ p "# (\[pun|weight|] -> weight &= 11 >< 14))% %%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%)% pno % pName % color % weight % city %)%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a)% P1 % Nut % Red % 12 % 1 % London %)% P5 % Cam % Blue % 12 % 1 % Paris %)%%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%1 5 &= 5 >=< 9True2 5 &= 5 "e< 9True3 5 &= 5 ><= 9False4 5 &= 5 >"d 9False5Synonym for <=6Synonym for >=7Synonym for /=8Average of a list of values1let _qtys a = Label .=. a :: Tagged "qtys" Double5pt$ group sp (rHdr (pno,qty)) (_qtys . avg . agg qty)&% %%%%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%&% qtys :: Double % sno :: String %&%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a&% 200.0 % S3 %&% 216.66666666666666 % S1 %&% 300.0 % S4 %&% 350.0 % S2 %&%%%%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%,Note the explicit type in the definition of _qtys2; it is neccessary to provide a specific type for pt to format it as a table, even when an expression would otherwise be correct without this. The following would blow up if prefixed with "pt$":6group sp (rHdr (pno,qty)) ((qtys .=.) . avg . agg qty)fromList [Record{qtys=200.0,sno="S3"},Record{qtys=216.66666666666666,sno="S1"},Record{qtys=300.0,sno="S4"},Record{qtys=350.0,sno="S2"}]9VMinimum of several values, defaulting to the second argument if there are no elements.Apt$ group sp (rHdr (pno,qty)) ((qtys .=.) . (`minx` 0) . agg qty)#% %%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%#% qtys :: Integer % sno :: String %#%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a#% 100 % S1 %#% 200 % S3 %#% 200 % S4 %#% 300 % S2 %#%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%:UMaximum of several values, defaulting to the second argument if there are no elementsApt$ group sp (rHdr (pno,qty)) ((qtys .=.) . (`maxx` 0) . agg qty)#% %%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%#% qtys :: Integer % sno :: String %#%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a#% 200 % S3 %#% 400 % S1 %#% 400 % S2 %#% 400 % S4 %#%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%()*+,-./0123456789:()*+,-./0123456789:()+*,-./0123456789:()*+,-./0123456789:(*+Ordering functions Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone(4?Takes any set (including that of a relation) and results in a list of the elements it consists of ordered by the given predicate@Takes any set (including that of a relation) and results in a list of the elements it consists of ordered on the given key function;<=>?@;<=>?@?@=>;<;<=>?@WDefinition and presentation of relational objects in terms of Haskell and HList records Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone '(3457>LAHaskRel type synonyms6A shorthand for "relational header", and a synonym of Data.HList.Record.LabelsE. The header of either an r-tuple or a relation in relational theory.CA synonym for Set ( RTuple a ).D:A "tuple as defined by the relational model", synonym for Data.HList.Record.E.A shorthand for "attribute", and a synonym of  Data.Tagged.FFConstructs an r-tuple from tagged values. Supports only 0 to 6-tuples.Ypt$ rTuple (sno .=. "S1", sName .=. "Smith", status .=. (20::Integer), city .=. "London")H% %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%H% sno :: String % sName :: String % status :: Integer % city :: String %H%%%%%%%%%%%%%%%%%<%%%%%%%%%%%%%%%%%%<%%%%%%%%%%%%%%%%%%%%<%%%%%%%%%%%%%%%%%$H% S1 % Smith % 20 % London %H%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%GConstructs an r-tuple from a tuples of untagged values, where the labels and exact types are inferred from the context. Supports only 0 to 6-tuples.Spt$ ( rTuple' ("S1", "Smith", 20, "London") :: RTuple '[SNO, SName, Status, City] )...Result as for F.HNConstruct a relation value from a list of r-tuples of tagged values. Alias of  with a result restricted to CB, with a name from relational theory. Supports only 0 to 6-tuples.:{ dpt$ relation [rTuple (sno .=. "S1", sName .=. "Smith", status .=. (20::Integer), city .=. "London"),c rTuple (sno .=. "S2", sName .=. "Jones", status .=. (10::Integer), city .=. "Paris")]:}H% %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%H% sno :: String % sName :: String % status :: Integer % city :: String %H%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%aH% S1 % Smith % 20 % London %H% S2 % Jones % 10 % Paris %H%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%IConstruct a relation value from a list of tuples of untagged values, where the labels and exact types are inferred from the context. Supports only 0 to 6-tuples.wpt$ ( relation' [("S1", "Smith", 20, "London"), ("S2", "Jones", 10, "Paris")] :: Relation '[SNO, SName, Status, City] )Result as for H.J Alias of Data.HList.HList.hFromTupleF with a name that is more descriptive for the purpose of constructing Labels, which are employed as relational headings, from Haskell tuples of label values. When labels have been defined it permits expressing:>pt$ p `project` (undefined :: Labels '["pno","pName","color"])As:(pt$ p `project` (rHdr (pno,pName,color))Supports 0 to 6-tuples.K Synonym of , for rename and L.LN-adic constructor of K statements, using mkRecord . hFromTuple. Only supports 0 to 6-tuples.let pnu = Label :: Label "pnu"$let colour = Label :: Label "colour"(:t nAs (pno `as` pnu, color `as` colour)%nAs (pno `as` pnu, color `as` colour)J :: Record '[Tagged "pno" (Label "pnu"), Tagged "color" (Label "colour")]M[Order-agnostic equality operation, which is neccessary for comparison correct for r-tuples.NwRearrange a set of HList records to context. From the perspective of relational theory this is a presentation function.ORearrange a set of HList records to the order of a set given by an argument. The value of the second argument will be ignored. From the perspective of relational theory this is a presentation function.PDGives the body of a relation as a list. This will untag the values. Q'The nullary relation with an empty body pt$ tableDum% %%%% %%^%P%P%a%%%%relation [] == tableDumTrueR(The nullary relation of cardinality one. pt$ tableDee% %%%% %%^%P%P%a% %%%%% relation [rTuple ()] == tableDeeTruerelation' [()] == tableDeeTrueS}The empty set without an explicit type. In a relational context this is a relation with an empty body and no defined heading.7relation [rTuple (sno .=. "S1", status .=. 5)] == emptyFalseNote how the example for Q won't work for S and vice-versa.T:Prints a tabular representation of an r-tuple or relation.UQPrints a tabular representation of an r-tuple or relation, with type information.V Synonym of T.W Synonym of U.XSpecialization of *Database.HaskRel.HFWTabulation.showHTypeTS- that employs HaskRel specific type-synonyms.Y-Shows a tabular representation of a relation.HaskRel type synonyms ABCDEFGHIJKLM NO!PQRSTUVWXY"#ABCDEFGHIJKLMNOPQRSTUVWXYEDCFGHIJKLQRSMPNOABTUVWXYABCDEFGHIJKLM NO!PQRSTUVWXY"#2Relation variable definition and support functions Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone(4>N ZRelation variable reference. This type has a phantom type variable, which generally calls for the type to be explicity specified: I s = Relvar "SuppliersPartsDB/S.rv" :: Relvar '[SNO, SName, Status, City]]WGives the type a relvar results in. Note that the value this results in will always be  undefined.^0Converts a list of HLists into a relation value._MReads a relation value from a string containing HLists of unpacked attribues.Dpt$ ( readRel "H[\"S1\",10],H[\"S2\",50]" :: Relation '[SNO,Status])%% %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%%%%%% sno :: String % status :: Integer %%%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a%% S1 % 10 %%% S2 % 50 %%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%%%%%$5Reads a relation value of a given type from a string.`GRead a relation variable from the file referenced by the first argumenta7Prints a relation as a list without the outer brackets.b)Writes a body of a relvar to a given filec:Writes a body of a relvar to a given relvar file reference Z[\]^_$`abc%& Z[\]^_`abc Z[\]_`^bca Z[\]^_$`abc%&2Relational algebra, including supporting functions Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone'(3457>KLN)dGFailure class restricting a type-level operation to a non-empty result.'5Rename of multiple labels, preserving original order.let aRecord = ((Tagged 1 :: Tagged "g" Int) .*. (Tagged 2 :: Tagged "a" Int) .*. (Tagged 3 :: Tagged "i" Int) .*. (Tagged 4 :: Tagged "c" Int) .*. (Tagged 5 :: Tagged "e" Int) .*. emptyRecord)aRecordRecord{g=1,a=2,i=3,c=4,e=5}rhRenameLabels ((Label::Label "c") .=. (Label::Label "d") .*. (Label::Label "a") .=. (Label::Label "b") .*. emptyRecord) aRecordRecord{g=1,b=2,i=3,d=4,e=5}f)Rename multiple attributes of a relation.let pnu = Label :: Label "pnu"$let colour = Label :: Label "colour":rPrint$ p' `rename` nAs( pno `as` pnu, color `as` colour )*% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%,%%%%%%%%%*% pnu % pName % colour % weight % city %*%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a*% P1 % Nut % Red % 12 % 1 % London %...g Renames a single attribute. See  hExtends the given relation with the r-tuple resulting from the second argument. Existing attributes with the same name will be replaced.See  .iExtends the given relation with the attribute resulting from the second argument. If an attribute with the same name exists then it will be replaced. This allows for the function of the second argument to be simpler.See  .jZDisjoint extension. Extends the given relation with the result of the second argument, as h&, but without deleting any that exist.See  .kpDisjoint extension of a single attribute. Extends the given relation with the result of the second argument, as h', but without deleting any that exist. l# cannot already have any attribute e.See  .lExtends the first given relation with an attribute resulting from imaging each tuple of said relation against the second given relation. This gives a superset of the information given by SQL RIGHT OUTER JOIN.See  .m`Restricts the given relation according to the given predicate. Note that this is the well known WHEREp operator of both SQL and Tutorial D, but since "where" is a reserved keyword in Haskell it is named "restrict".See  .n1Projects the given relation on the given heading.See  .oZProjects the given relation on the heading of said given relation minus the given heading.See  .p(Performs a union of the given relations.See  .qPerforms a disjoint union between the two relvars. This is a union of disjoint relations, where a runtime error is raised if the operands are not disjoint.See  .r"The intersection of two relations.Note how the name is different from Data.Set, where the comparable function is named "intersection". This is due to it being referred to as "intersect" in material describing the relational model; specifically named "INTERSECT" in Tutorial D.See  .s The difference of two relations.vThe "minus" term is used in material describing relational theory; specifically Tutorial D names the operator "MINUS".See  .t3The difference of two relations. This differs from ss in that the attribute order of the second argument takes precedence, which is neccessary to swap precedence since s7 is non-commutative. This function is as such equal to s] as far as relational theory is concerned, the difference is on a lower level of abstraction.u+Exclusive union, aka. symmetric difference.See  .v3Performs a natural join of the two given relations.See  .w Alias of v.See  .xThe cartesian product of two relations. A specialized natural join; the natural join between two relations with disjoint headings.See  .yePerforms a natural join between two relations with intersecting headings. A specialized natural join.A join upon relations r1, r2 where the intersection of the heading of r1 and of r2 is not empty; the headings are not disjoint. This is the complement of xN that together with it forms a natural join; all that would be disallowed for times is allowed here and vice-versa. The name is what I quickly settled on, suggestions for a better one would be welcome. (Attribute-Intersecting Natural Join is another candidate.)MThis function doesn't have a specific identity value, although it holds that r `interJoin` r = rSee  .z Alias of y. See  .{SPerforms a semi-join of the first given relation against the second given relation.See  .| Alias of {. See  .}YPerforms a semi-difference of the first given relation against the second given relation.Aka. antijoin:2Also known, a trifle inappropriately, as antijoin.9Chris Date 2011, SQL and Relational Theory 2nd ed. p. 133See  .~ Alias of }. See  !.4The image of a relation corresponding to an r-tuple.dAn application of the first argument only, an r-tuple, to this function yields what is known as the !! operator in Tutorial D."let qtySum = Label::Label "qtySum":{ rPrint$ s'" `project` (rHdr (sno))) `extendA` (\ (image -> ii) ->& qtySum .=.X ( sum $ aggU $ ii ( sp' `project` (rHdr (sno,qty)) ) ) ) :}See  ".]Groups the given attributes of the given relation into a given new relation valued attribute.3As the Tutorial D GROUP operator, not SQL GROUP BY.See  #.UGroups the given relation on all but the given attributes into a given new attribute.See  $.3Ungroups the given attribute of the given relation.let pq = (Label :: Label "pq")8sp' == ungroup ( group sp' (rHdr (pno,qty)) (pq .=.)) pqTrueNote the difference to  %, which requires  & for relational comparison. Summarize.See  '.(Auto-summarization. A specialization of / with the same source and destination relation.See  (.&Gives the cardinality of the argument.1Gives whether the given argument is empty or not.BTests whether the second argument is a proper subset of the first.See  ).;Tests whether the second argument is a subset of the first.See  *.Right-fold of an attribute of a relation (although a "right" fold doesn't make sense in the context of the relational model). Note that the value of the third argument, att%, is not used and may be "undefined".See  +.1Attribute value aggregation, a specialization of e that aggregates the values of a single attribute into a list of the values the attribute type wraps.+Note that the value of the first argument, att%, is not used and may be "undefined".sum $ agg qty sp'3100See  ,.0Right-fold of the attribute of a unary relation.See  -.MAggregation of the single attribute of a unary relation. A specialization of , and thus in turn of i, that aggregates the single attribute of a unary relation, without requiring the name of that attribute.'sum $ aggU $ sp' `project` (rHdr (qty))1000See  ..8d(e)'fghijklmnopqrstuvw*xyz{|+,}~-./012345*Ndefghijklmnopqrstuvwxyz{|}~*fhmnovwx{|}~pqrsuyzjltikgNde7d(e)'fghijklmnopqrstuvw*xyz{|+,}~-./012345Relational assignment Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone '(4>LN GWrites a relation value to a relvar file, replacing the existing value.Inserts a relation into a relvar. This differs from SQL's INSERT; the relvar is updated to the union of the relvar and the relation value given as arguments.See  /.\Disjoint insert. Closer to SQL INSERT, except that this will never insert a duplicate tuple.See  0.IUpdates tuples of a relvar that match the given predicate. As SQL UPDATE.kupdate sp (\ [pun|pno|] -> pno == "P2" || pno == "P3" ) (\ [pun|qty|] -> _qty ( qty - 25 ) .*. emptyRecord) 0Updated 5 of 12 tuples in SuppliersPartsDB/SP.rv"*SuppliersPartsExample> rPrint$ sp% %%%%%%,%%%%%%,%%%%%%% sno % pno % qty %%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%a% S1 % P1 % 300 %% S1 % P2 % 175 %% S1 % P3 % 375 %% S1 % P4 % 200 %...NNote how the cardinality of the relvar will be equal or lower after an update: assign sp sp'(Value assigned to SuppliersPartsDB/SP.rvcount sp12rupdate sp (\[pun|pno|] -> pno == "P1" || pno == "P2" || pno == "P3") (\_ -> _pno "P1" .*. _qty 50 .*. emptyRecord)0Updated 7 of 12 tuples in SuppliersPartsDB/SP.rvcount sp9Updates all tuples of a relvar. The second argument is a function that results in an attribute, making for a simpler function than for .ZupdateA sp (\ [pun|pno|] -> pno == "P2" || pno == "P3" ) (\ [pun|qty|] -> _qty $ qty - 25)0Updated 5 of 12 tuples in SuppliersPartsDB/SP.rv:Updates tuples of a relvar that match the given predicate.,In SQL and Tutorial D both the predicate of UPDATEm is an optional clause, but optional clauses isn't idiomatic Haskell, hence this separate updateAll function.\updateAll sp (\ [pun|qty pno|] -> _qty ( qty - 25 ) .*. _pno ( pno ++ "X" ) .*. emptyRecord)+Updated 12 tuples in SuppliersPartsDB/SP.rv*SuppliersPartsExample> pt sp2% %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%2% sno :: String % pno :: String % qty :: Integer %2%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a2% S1 % P1X % 275 %...Updates all tuples of a relvar. The second argument is a function that results in an attribute, making for a simpler function than for ./updateAllA sp (\ [pun|qty|] -> _qty $ qty - 50)+Updated 12 tuples in SuppliersPartsDB/SP.rv rPrint$ sp2% %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%%2% sno :: String % pno :: String % qty :: Integer %2%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a2% S1 % P1 % 250 %...oDeletes a specified subset of a relvar. Note that this is not SQL DELETE, but instead a generalization thereof.See  1.Performs an inclusive delete against a relvar. Also not SQL DELETE. This will fail if the second argument is not a subset of the relation value identified by the relation variable reference.See  2.#Delete by predicate, as SQL DELETE.let newProd = relation [rTuple (pno .=. "P7", pName .=. "Baloon", color .=. "Red", weight .=. (-5 :: Rational), city .=. "Berlin")]insert p newProd1Inserted 1 of 1 tuples into SuppliersPartsDB/P.rv(deleteP p (\ [pun|pno|] -> pno == "P7" )+Deleted 1 tuples from SuppliersPartsDB/P.rv6789:;<=>?@A 6789:;<=>?@A "Support for relational expressions Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone '(3457>KL6"The class of relational assignment#Relational IO function application.(The class of relational dyadic operators)The class of relational monadic operators&Unary relational function application.&Rename given attributes of a relation.let pnu = Label :: Label "pnu"$let colour = Label :: Label "colour"9rPrint$ p `rename` nAs( pno `as` pnu, color `as` colour )*% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%,%%%%%%%%%*% pnu % pName % colour % weight % city %*%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a*% P1 % Nut % Red % 12 % 1 % London %...Note that due to an implementation disorder this always results in an IO operation, even on values. This is not an intentional limit and will hopefully be removed in the future. If this is not acceptable (for instance inside  and % functions), then one has to rely on #, which renames a single attribute.Renames one attribute."let sCity = Label :: Label "sCity"%rPrint$ s `renameA` (city `as` sCity) !% %%%%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%!% sCity % sno % sName % status %!%^%P%P%P%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% Athens % S5 % Adams % 30 %!% London % S1 % Smith % 20 %!% London % S4 % Clark % 20 %!% Paris % S2 % Jones % 10 %!% Paris % S3 % Blake % 30 %!%%%%%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%%This only accepts a single pair of labels, the label to rename and the new label, in contrast to Tutorial D rename which takes a set of from-to pairs.renameA can, unlike  , be used by  and ::{ do spx <- readRelvar sp/ rPrint$ sp `restrict` (\( image -> ii ) ->\ count ( ii $ renameA (renameA spx (sno `as` sn)) (pno `as` pn) ) > 2):}...Extends the given relation with the r-tuple resulting from the second argument. Existing attributes with the same name will be replaced.BThe simplest form (aside from a no-op), extend with one attribute:_rPrint$ extend p (\ [pun|weight|] -> (Label :: Label "gmwt") .=. weight * 454 .*. emptyRecord )4% %%%%%%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%4% gmwt % pno % pName % color % weight % city %4%^%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a4% 5448 % 1 % P1 % Nut % Red % 12 % 1 % London %...4% 8626 % 1 % P6 % Cog % Red % 19 % 1 % London %4%%%%%%%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%xWhen replacing an attribute with extend one must take care not to cause a naming collision. Using pattern matching with case ... of ... one can pass values from one context to another with Haskell tuples, and reuse variable names, although this does require some duplication. It is also possible to use pun! to build the output, instead of .*. and  emptyRecord%. Add one attribute, replace another:~rPrint$ extend p (\[pun|weight color|] -> case (weight + 10, color ++ "-ish") of (weight, altColor) -> [pun|weight altColor|])5% %%%%%%%%%,%%%%%%%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%5% weight % altColor % pno % pName % color % city %5%^%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a5% 22 % 1 % Blue-ish % P5 % Cam % Blue % Paris %...5% 29 % 1 % Red-ish % P6 % Cog % Red % London %5%%%%%%%%%%4%%%%%%%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%Lining this up with the EXTEND( operator of Tutorial D, we can imagine  case (a, b) of (a', b')  as a form of  { a' := a , b' := b } + (though we can hardly equate them), while pun< is needed to unpack and pack this from and to the r-tuples.Also note that if an attribute is replaced then the cardinality of the result will be equal or lower than that of the argument.count sp12Icount $ sp `extend` (\_ -> sno .=. "S0" .*. pno .=. "P0" .*. emptyRecord)4It is also notable that since HaskRel is not based on SQL but on relational theory as defined by Chris Date et al today, and explicitly does not have support for nulls and outer joins (as specified in [1] chapter 4), extend< is employed to assemble the information SQL assembles with  OUTER JOIN. The following command (a variant of the first query on [1] page 154) gives a result that includes the information given by SQL RIGHT OUTER JOIN::{do sp' <- readRelvar spF rPrint$ extend s (\ (image -> ii) -> pq .=. ii sp' .*. emptyRecord):}1% %%%%%%%%%%%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%1% pq % sno % sName % status % city %1%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a1% % %%%%%%,%%%%%% % S5 % Adams % 30 % Athens %1% % pno % qty % % % % % %1% %^%P%P%P%P%P%j%P%P%P%P%P%a % % % % %1% %%%%%%%4%%%%%% % % % % %1% % %%%%%%,%%%%%% % S1 % Smith % 20 % London %1% % pno % qty % % % % % %1% %^%P%P%P%P%P%j%P%P%P%P%P%a % % % % %1% % P1 % 300 % % % % % %1% % P2 % 200 % % % % % %...NSee material about extend, image relations and RVAs in [1] chapter 7 for more.LNote the additional plumbing required to employ relvars inside the function extend takes; the function V has been created to provide a more convenient way to express this, specializing upon extend and .Extends the given relation with the attribute resulting from the second argument. If an attribute with the same name exists then it will be replaced. This allows for the function of the second argument to be simpler.Where c. is an expression yielding a single attribute:"extend a (\b -> c .*. emptyRecord)Is equivalent to:extendA a (\b -> c);The following has the same result as the first example for : let gmwt = (Label::Label "gmwt");rPrint$ extendA p (\[pun|weight|] -> gmwt .=. weight * 454)Note that if one wants to alter the values of an existing attribute then one has to avoid a name collision. The most convenient option will most often be having a constructor function or label constant with a different name from the actual label:-let _weight a = (Label::Label "weight") .=. a;rPrint$ extendA p (\[pun|weight|] -> _weight $ weight + 10)ZDisjoint extension. Extends the given relation with the result of the second argument, as ', but without deleting any that exist. pDisjoint extension of a single attribute. Extends the given relation with the result of the second argument, as ', but without deleting any that exist. l# cannot already have any attribute e.`Restricts the given relation according to the given predicate. Note that this is the well known WHEREp operator of both SQL and Tutorial D, but since "where" is a reserved keyword in Haskell it is named "restrict".6rPrint$ p `restrict` (\[pun|weight|] -> weight < 17.5))% %%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%)% pno % pName % color % weight % city %)%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a)% P1 % Nut % Red % 12 % 1 % London %...)% P5 % Cam % Blue % 12 % 1 % Paris %)%%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%1Projects the given relation on the given heading.'rPrint$ p `project` (rHdr (color,city))% %%%%%%%%,%%%%%%%%%% color % city %%^%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a% Blue % Oslo %% Blue % Paris %% Green % Paris %% Red % London %%%%%%%%%%4%%%%%%%%%ZProjects the given relation on the heading of said given relation minus the given heading.'rPrint$ p `projectAllBut` (rHdr (city)) % %%%%%%,%%%%%%%%,%%%%%%%%,%%%%%%%%% % pno % pName % color % weight % %^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a % P1 % Nut % Red % 12 % 1 % % P2 % Bolt % Green % 17 % 1 % % P3 % Screw % Blue % 17 % 1 % % P4 % Screw % Red % 14 % 1 % % P5 % Cam % Blue % 12 % 1 % % P6 % Cog % Red % 19 % 1 % %%%%%%%4%%%%%%%%4%%%%%%%%4%%%%%%%%%]Groups the given attributes of the given relation into a given new relation valued attribute.3As the Tutorial D GROUP operator, not SQL GROUP BY.let pq = (Label :: Label "pq")&pt$ group sp (rHdr (pno,qty)) (pq .=.) 6% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%6% pq :: Relation '["pno","qty"] % sno :: String %6%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a6% % %%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%% % S1 %6% % pno :: String % qty :: Integer % % %6% %^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a % %6% % P1 % 300 % % %...6%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%Note that the last argument is a function that tags any value with a label; an attribute constructor. This is different from Tutorial D GROUP, which just takes the equivalent of a label, but as long as an attribute constructor is provided it will function the same way. Here is what we get if we aggregate the relation we get as the argument to the receiving function with 7, instead of just supplying an attribute constructor ("(pq .=.) " above):"let qtys = (Label :: Label "qtys")pt$ group sp (rHdr (pno,qty)) ' ((qtys .=.) . agg qty)-% %%%%%%%%%%%%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%-% qtys :: [Integer] % sno :: String %-%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a-% [200] % S3 %-% [200,300,400] % S4 %-% [300,200,400,200,100,100] % S1 %-% [300,400] % S2 %-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%P"Get the quantities of items in stock for those suppliers that supply anything"::pt$ group sp (rHdr (pno,qty)) ((qtys .=.) . sum . agg qty)#% %%%%%%%%%%%%%%%%%%,%%%%%%%%%%%%%%%%#% qtys :: Integer % sno :: String %#%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%a#% 200 % S3 %#% 700 % S2 %#% 900 % S4 %#% 1300 % S1 %#%%%%%%%%%%%%%%%%%%%4%%%%%%%%%%%%%%%%5Note the difference between this and the example for =. These last two examples may be more clearly expressed with I, since we then specify the attributes that the resulting type will have.UGroups the given relation on all but the given attributes into a given new attribute.(pt$ groupAllBut sp (rHdr (sno)) (pq .=.)%Same result as the first example for .=pt$ groupAllBut sp (rHdr (sno)) ((qtys .=.) . sum . agg qty )$Same result as the last example for .CUngroups the given relation valued attribute of the given relation.Psp `rEq` ungroup ( group sp (rHdr (pno,qty)) (pq .=.)) (undefined :: Label "pq")True(Self-summarization, the special case of S where the source and target relations is the same. This is closer to SQL GROUP BY.Tpt$ aSummarize sp (rHdr (pno,qty)) (\r -> qty .=. sum ( agg qty r ) .*. emptyRecord) Same result as last example for .&Gives the cardinality of the argument.count sp121Gives whether the given argument is empty or not. isEmpty spFalseRight-fold of an attribute of a relation (although a "right" fold doesn't make sense in the context of the relational model). Note that the value of the third argument is not used and may be "undefined".rafoldr (+) 0 qty sp3100rafoldr (*) 1 qty sp276480000000000000000000000001Attribute value aggregation, a specialization of e that aggregates the values of a single attribute into a list of the values the attribute type wraps.MNote that the value of the first argument is not used and may be "undefined".:{ do sp' <- readRelvar sp' putStrLn $ show $ sum $ agg qty sp':}31000Right-fold of the attribute of a unary relation.*rafoldrU (+) 0 $ sp `project` (rHdr (qty))1000*rafoldrU (*) 1 $ sp `project` (rHdr (qty)) 2400000000MAggregation of the single attribute of a unary relation. A specialization of , and thus in turn of i, that aggregates the single attribute of a unary relation, without requiring the name of that attribute.:{ do sp' <- readRelvar sp= putStrLn $ show $ sum $ aggU $ sp' `project` (rHdr (qty)) :}1000ZAggregates an attribute and applies a function to the result of that. A specialization of .MNote that the value of the first argument is not used and may be "undefined".rAgg qty sp sum3100oAggregates the attribute of a unary relation and applies a function to the result of that. A specialization of .%rAggU (sp `project` (rHdr (qty))) sum10004The image of a relation corresponding to an r-tuple.dAn application of the first argument only, an r-tuple, to this function yields what is known as the !! operator in Tutorial D.+rPrint$ rTuple (sno .=. "S2", pno .=. "P1") % %%%%%%,%%%%%% % sno % pno % %%%%%%%<%%%%%%$ % S2 % P1 % %%%%%%%4%%%%%%.rPrint$ rTuple (sno .=. "S2", pName .=. "Nut")% %%%%%%,%%%%%%%%% sno % pName %%%%%%%%<%%%%%%%%$% S2 % Nut %%%%%%%%4%%%%%%%%6rPrint$ rTuple (sno .=. "S2", pno .=. "P1") `image` sp% %%%%%%% qty %%^%P%P%P%P%P%a% 300 %%%%%%%%9rPrint$ rTuple (sno .=. "S2", pName .=. "Nut") `image` sp % %%%%%%,%%%%%% % pno % qty % %^%P%P%P%P%P%j%P%P%P%P%P%a % P1 % 300 % % P2 % 400 % %%%%%%%4%%%%%%Image relations give rise to summarization. Here is a form of the query, "get the quantities of items in stock for all suppliers"::{do sp' <- readRelvar sp# rPrint$ s `project` (rHdr (sno))* `extendA` (\ (image -> ii) ->R (Label::Label "qtySum") .=. ( sum $ agg qty $ ii sp' ) ):}% %%%%%%%%%,%%%%%%% qtySum % sno %%^%P%P%P%P%P%P%P%P%j%P%P%P%P%P%a% 0 % S5 %% 200 % S3 %% 700 % S2 %% 900 % S4 %% 1300 % S1 %%%%%%%%%%%4%%%%%%-Note how view patterns are used to build the ii& operator, equivalent of Tutorial D's !!5 operator. An equivalent form of the lambda would be: I (\t -> (Label::Label "qtySum") .=. ( sum $ agg qty $ t `image` sp' )) See  for a similar example.<Gives whether a given r-tuple is member of a given relation.;member (rTuple(sno .=. "S3", qty .=. 200, pno .=. "P2")) spTrueBGives whether a given r-tuple is not a member of a given relation.>notMember (rTuple(sno .=. "S3", qty .=. 200, pno .=. "P2")) spFalse'Binary relational function application.flet newSups = ( relation [rTuple (sno .=. "S6", sName .=. "Nena", city .=. "Berlin", status .=. 40)] )dyaOp (/=) s newSupsTrueRelational equality.Esp `rEq` (relation [rTuple(sno .=. "S2", qty .=. 400, pno .=. "P2")])False,The natural join of the two given relations.rPrint$ sp `naturalJoin` s-% %%%%%%,%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%-% sno % pno % qty % sName % status % city %-%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a-% S1 % P1 % 300 % Smith % 20 % London %...-% S4 % P5 % 400 % Clark % 20 % London %-%%%%%%%4%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%% Alias of .The cartesian product of two relations. A specialized natural join; the natural join between two relations with disjoint headings.XrPrint$ ( sp `projectAllBut` (rHdr (city)) ) `times` ( s `projectAllBut` (rHdr (city)) )...: No instance for (Fail (DuplicatedLabel (Label "sno")))# arising from a use of times VrPrint$ ( sp `projectAllBut` (rHdr (sno)) ) `times` ( s `projectAllBut` (rHdr (sno)) ) '% %%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%'% pno % qty % sName % status % city %'%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a'% P1 % 300 % Adams % 30 % Athens %'% P1 % 300 % Blake % 30 % Paris %...'% P6 % 100 % Jones % 10 % Paris %'% P6 % 100 % Smith % 20 % London %'%%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%^The natural join between two relations with intersecting headings. A specialized natural join.A join upon relations r1, r2 where the intersection of the heading of r1 and of r2 is not empty; the headings are not disjoint. This is a complement of 7 within natural join; all that would be disallowed for times is allowed here and vice-versa. The name is what I quickly settled on, suggestions for a better one would be welcome. (Attribute-Intersecting Natural Join is another candidate.)MThis function doesn't have a specific identity value, although it holds that r `interJoin` r = rZrPrint$ ( sp `projectAllBut` (rHdr (sno)) ) `interJoin` ( s `projectAllBut` (rHdr (sno)) )...* Overlapping instances for NotEmpty '[]' arising from a use of interJoin rPrint$ sp `interJoin` s -% %%%%%%,%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%-% sno % pno % qty % sName % status % city %-%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a-% S1 % P1 % 300 % Smith % 20 % London %-% S1 % P2 % 200 % Smith % 20 % London %...-% S4 % P4 % 300 % Clark % 20 % London %-% S4 % P5 % 400 % Clark % 20 % London %-%%%%%%%4%%%%%%4%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%% Alias of Extends the first given relation with an attribute resulting from imaging each tuple of said relation against the second given relation. The following command gives a result that includes the information given by SQL RIGHT OUTER JOIN:rPrint$ imageExtendL s sp pq 1% %%%%%%%%%%%%%%%%,%%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%1% pq % sno % sName % status % city %1%^%P%P%P%P%P%P%P%P%P%P%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a1% % %%%%%%,%%%%%% % S5 % Adams % 30 % Athens %1% % pno % qty % % % % % %1% %^%P%P%P%P%P%j%P%P%P%P%P%a % % % % %1% %%%%%%%4%%%%%% % % % % %1% % %%%%%%,%%%%%% % S1 % Smith % 20 % London %1% % pno % qty % % % % % %1% %^%P%P%P%P%P%j%P%P%P%P%P%a % % % % %1% % P1 % 300 % % % % % %1% % P2 % 200 % % % % % %... See also ', which this function specializes, and /, which it uses to perform this specialization.LThe semi-join of the first given relation against the second given relation.rPrint$ s `matching` sp!% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% S1 % Smith % 20 % London %!% S2 % Jones % 10 % Paris %!% S3 % Blake % 30 % Paris %!% S4 % Clark % 20 % London %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%% Alias of .aThe semi-difference of the first given relation against the second given relation. Aka. antijoin.rPrint$ s `notMatching` sp!% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% S5 % Adams % 30 % Athens %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%% Alias of .The union of two relations.flet newSups = ( relation [rTuple (sno .=. "S6", sName .=. "Nena", city .=. "Berlin", status .=. 40)] )rPrint$ s `union` newSups !% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% S1 % Smith % 20 % London %!% S2 % Jones % 10 % Paris %!% S3 % Blake % 30 % Paris %!% S4 % Clark % 20 % London %!% S5 % Adams % 30 % Athens %!% S6 % Nena % 40 % Berlin %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%The disjoint union between the relations. This is a union of disjoint relations, where a runtime error is raised if the arguments are not disjoint.:{ ( rPrint$ ( p' `project` (rHdr (city)) ) `dUnion`( ( s' `project` (rHdr (city)) ):}D% %*** Exception: Arguments to dUnion are not disjoint, intersection: % %%%%%%%%% % city % %^%P%P%P%P%P%P%P%P%a % London % % Paris % %%%%%%%%%%"The intersection of two relations.Note how the name is different from Data.Set, where it is named "intersection". This is due to it being referred to as "intersect" in material describing the relational model; specifically named "INTERSECT" in Tutorial D.let sX = ( relation [rTuple (sno .=. "S2", sName .=. "Jones", city .=. "Paris", status .=. 10), rTuple (sno .=. "S6", sName .=. "Nena", city .=. "Berlin", status .=. 40)] )rPrint$ s `intersect` sX % %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%% % sno % sName % status % city % %^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%a % S2 % Jones % 10 % Paris % %%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%YNotably, for any given relation values r1 and r2 that are of the same type it holds that: (r1 `intersect` r2 == r1 `naturalJoin` r2fWithin relational theory the natural join generalizes as such both intersection and cartesian product. The difference of two relations.vThe "minus" term is used in material describing relational theory; specifically Tutorial D names the operator "MINUS".rPrint$ s `minus` sX!% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% S1 % Smith % 20 % London %!% S3 % Blake % 30 % Paris %!% S4 % Clark % 20 % London %!% S5 % Adams % 30 % Athens %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%*Exclusive union, aka. symmetric differencerPrint$ s `xUnion` sX !% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a!% S1 % Smith % 20 % London %!% S3 % Blake % 30 % Paris %!% S4 % Clark % 20 % London %!% S5 % Adams % 30 % Athens %!% S6 % Nena % 40 % Berlin %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%9The summarization of the relations by the given function.let pct = Label :: Label "pct"drPrint$ summarize sp (s `project` (rHdr (sno))) (rHdr (pno)) (\r -> pct .=. count r .*. emptyRecord) % %%%%%%,%%%%%% % pct % sno % %^%P%P%P%P%P%j%P%P%P%P%P%a % 0 % S5 % % 1 % S3 % % 2 % S2 % % 3 % S4 % % 6 % S1 % %%%%%%%4%%%%%%nrPrint$ summarize sp (s `project` (rHdr (sno))) (rHdr (qty)) (\r -> qty .=. sum ( agg qty r ) .*. emptyRecord) % %%%%%%%,%%%%%%% qty % sno %%^%P%P%P%P%P%P%j%P%P%P%P%P%a% 0 % S5 %% 200 % S3 %% 700 % S2 %% 900 % S4 %% 1300 % S1 %%%%%%%%%4%%%%%%BTests whether the second argument is a proper subset of the first.wlet spX = relation [rTuple (sno .=. "S1", pno .=. "P4", qty .=. 200), rTuple (sno .=. "S2", pno .=. "P2", qty .=. 400)]spX `isProperSubsetOf` spTrue;Tests whether the second argument is a subset of the first.spX `isSubsetOf` spTrueGWrites a relation value to a relvar file, replacing the existing value. assign s s''Value assigned to SuppliersPartsDB/S.rvInserts a relation into a relvar. This differs from SQLs INSERT; the relvar is updated to the union of the relvar and the relation value given as arguments.blet newSups = relation [rTuple (sno .=. "S6", sName .=. "Nena", city .=. "Berlin", status .=. 40)]insert s newSups1Inserted 1 of 1 tuples into SuppliersPartsDB/S.rvinsert s newSups1Inserted 0 of 1 tuples into SuppliersPartsDB/S.rv rPrint$ s!% %%%%%%,%%%%%%%%,%%%%%%%%%,%%%%%%%%%!% sno % sName % status % city %!%^%P%P%P%P%P%j%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%j%P%P%P%P%P%P%P%P%a...!% S6 % Nena % 40 % Berlin %!%%%%%%%4%%%%%%%%4%%%%%%%%%4%%%%%%%%%\Disjoint insert. Closer to SQL INSERT, except that this will never insert a duplicate tuple.GdInsert sp $ relation [rTuple (sno .=. "S6", pno .=. "P7", qty .=. 99)]-Inserted 1 tuples into SuppliersPartsDB/SP.rvdInsert sp $ relation [rTuple (sno .=. "S6", pno .=. "P7", qty .=. 99), rTuple (sno .=. "S4", pno .=. "P4", qty .=. 300), rTuple (sno .=. "S7", pno .=. "P8", qty .=. 200)]]*** Exception: Unique constraint violation, tuples already present in SuppliersPartsDB/SP.rv:% %%%%%%,%%%%%%,%%%%%%% sno % pno % qty %%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%a% S4 % P4 % 300 %% S6 % P7 % 99 %%%%%%%%4%%%%%%4%%%%%%oDeletes a specified subset of a relvar. Note that this is not SQL DELETE, but instead a generalization thereof.delete s newSups+Deleted 1 tuples from SuppliersPartsDB/S.rvPerforms an inclusive delete against a relvar. Also not SQL DELETE. This will fail if the second argument is not a subset of the relation variable.iDelete sp $ relation [rTuple (sno .=. "S6", pno .=. "P7", qty .=. 99), rTuple (sno .=. "S4", pno .=. "P4", qty .=. 300), rTuple (sno .=. "S7", pno .=. "P8", qty .=. 200)]A*** Exception: Tuples not found in relvar SuppliersPartsDB/SP.rv:% %%%%%%,%%%%%%,%%%%%%% sno % pno % qty %%^%P%P%P%P%P%j%P%P%P%P%P%j%P%P%P%P%P%a% S7 % P8 % 200 %%%%%%%%4%%%%%%4%%%%%%SBCDEFGHIJKLMNOPQRSTUVWXBdBdHBCDEFGHIJKLMNOPQRSTUVWX UFunctions pertaining to relational theory or set theory named with unicode characters Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone(4>LN Natural join.rPrint$ sp " s...As . Semijoin.rPrint$ s " sp...As .Left semijoin. As (flip ).Semidifference.rPrint$ s % sp...As .Left semidifference. As (flip ).YTimes. The special case of natural join where the headings of the relations are disjoint.PrPrint$ ( sp `projectAllBut` (rHdr (sno)) ) ( s `projectAllBut` (rHdr (sno)) )...As .{Attribute intersecting natural join. The special case of natural join where the headings of the relations are not disjoint.Using the "box times" or "squared times" (U+22A0) symbol is my own solution. As with the name "(attribute) intersecting natural join" suggestions are welcome.As mentioned in #Database.HaskRel.Relational.Algebrak, this operation doesn't have a single identity value, although it holds that for any given relation value r,  r " r = rrPrint$ sp " s...As . Restriction.Note that the symbol used here is the divisor symbol, which looks the same but is distinct from the vertical bar, or pipe. However, since the vertical bar is used in Haskell for different purposes and is for that reason not a valid infix operator symbol, this is used instead.-rPrint$ p "# (\[pun|weight|] -> weight < 17.5)...As .Union.drPrint$ s "* ( relation [rTuple (sno .=. "S6", sName .=. "Nena", status .=. 40, city .=. "Berlin")] )...As . Intersection.let sX = ( relation [rTuple (sno .=. "S2", sName .=. "Jones", status .=. 10, city .=. "Paris"), rTuple (sno .=. "S6", sName .=. "Nena", status .=. 40, city .=. "Berlin")] )rPrint$ s ") sX...As .Minus.9Note that this is the difference symbol, not a backslash.rPrint$ s " sX...As . Projection.Note that no matter how greek it is  is still a character, and Haskell therefore treats it as a prefix operator, which is in line with how it is employed.rPrint$  (rHdr (color,city)) p...As ), but note how the operands are reversed.Relational assignment operator.This uses the COLON EQUALS UTF-8 character (&#8788;), the ASCII variant := wouldn't be allowed in Haskell since it starts with a colon.s "T s'As .Is proper subset.wlet spX = relation [rTuple (sno .=. "S1", pno .=. "P4", qty .=. 200), rTuple (sno .=. "S2", pno .=. "P2", qty .=. 400)]spX " spTrueAs . Is subset of.spX " spTrueAs .Left is proper subset of. As (flip ).Left is subset of. As (flip ). Cardinality.(#) sp12As . Is member of.9let spX = rTuple(sno .=. "S3", qty .=. 200, pno .=. "P2")spX " spTrueAs .Is not member of.spX " spFalseAs .Is member of left. As (flip ).Is not member of left. As (flip ).)The empty set. Note that this is neither tableDum nor tableDee.$(relation [] :: Relation '[]) == (")True5relation [rTuple (sno .=. "S1", status .=. 5)] == (")False3KA re-export of the modules that form the HList record based HaskRel library Thor Michael Stre, 2015)GPL v2 without "any later version" clausethormichael t gmail dt com experimentalNone(YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]`dZ[\]`456789:8;<8;<==>>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnooppqrsstuvwxyz{|}~    !"#$%'()*+,-./012               # $ % ( + , - . " &         !      ' ) * / 0 1 2 89        ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 567867967967:67:6;<6;=6;>6;?@AB@AC@AC8DE8DF8DG8DH8DI8DJ8DK8DL8DM8DN8DO8DP8DQ8DR8DS8DT8DU8DV8DW8DX8DY8DZ8DZ8D[8D\8]^8]_8`a8`b8`c8`d8`e8`f8`g8`h8`i8`j8`k8lm8ln8lo8lp8qr8qs8qt8qu8qv8qw8qx8qy8qz8q{8q|8q}8q~8q8q8q8q8q8q898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989888888888888888888888888888888888888888888888888888888888888888888 8 8 8 8 888888888888888888888 8!8"8#8$8%8&8'8(8)8*8+8,8-8.8/808182838485868788898:8;8<8=8>8?8@8A8B8B8C8D8E8F8G8H8I8J8K8L8M8N8O8P8Q8R8S8T8U8V8W8X8Y8Z8[8\8]8^8_8_8`8a8b8c8c8d8e8f8g8h8i8j8k8l8m8n8o8p8q8r8s8t8u8v8w8x8y8y8z8{8|8}8~8888888888888888888888888888888888888888888888888;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;8;@A@A@A@A@A@A@A@A@A@A8D8D8D8D8D8D8D8D8D8D8D8D8D88888`8` 8` 8` 8` 8` 8`8`8`8`8l8l8l8l8l8l8l8q8q8q8q8q889 89!89"89#89$89%89&89'89(89)89*89+89,89-89.89/89089189289389489589689789889989:89;89<89=89>89?89@8AB8AC8AD8AE8F8G8H8I8J8K8L8M8N8O8P8Q8R8S8T8U8V8W8X8Y8Z8[8\8]8^8_8`8a8b8c8d8e8f8g8h8i8j8k8l8m8n8o8p8q8r8s8t8u8v8w8x8y8z8;{8;|8;}8;~8;8;8;8;8;8;8;8;8;8;HaskR_DLZpwX0r2QJH0Bu7zqKheiDatabase.HaskRel.FWTabulation&Database.HaskRel.Relational.DefinitionDatabase.HaskRel.HFWTabulationDatabase.HaskRel.SupportDatabase.HaskRel.Order$Database.HaskRel.Relational.Variable#Database.HaskRel.Relational.Algebra&Database.HaskRel.Relational.Assignment&Database.HaskRel.Relational.Expression#Database.HaskRel.Relational.UnicoderenameAextendextendAdExtenddExtendA imageExtendLrestrictproject projectAllButuniondUnion intersectminusxUnion naturalJoinnJointimes interJoiniJoinmatchingsemiJoin notMatchingsemiDiffimagegroup groupAllButungrouprEq summarize aSummarizeisProperSubsetOf isSubsetOfrafoldraggrafoldrUaggUinsertdInsertdeleteiDeleteDatabase.HaskRel.RDBMSmaxLenpresent1LineValuepresentNLineValue padTransposeHList_2F5tB30aW7XHy3xvccuVdLData.HList.RecordLabelsData.HList.FakePreludeLabelHPresentTypedRecAttrHPresentRecAttr FWPresent' fwPresent'fwPresentTyped' FWPresent fwPresentfwPresentTyped HFWPresenthfwPrint hfwPrintTypedhfwPrintTypedTSHListTypeSynonymhRecTS hRecSetTShTIPTS hTIPSetTSFWPPredHFWOther HFWStringHFWRecHFWTIP HFWTIPSetshowTRshowTRTSshowHListSetType showHTypeTSshowHRecSetTabprintHRecSetTabprintHRecSetTabTypedprintHRecSetTabTypedTS&= plusminus±+-between>=<=≥≤xBetween><>=<≥<><=>≤≤≥≠avgminxmaxxDescAscorderByorderOn HaskRelTSRelationRTupleAttrrTuplerTuple'relation relation'rHdrasnAs unordRecEq relRearrange relRearrange' bodyAsListtableDumtableDeeemptyrPrint rPrintTypedppt rShowType showRelTabRelvar relvarPath relvarType hListsToRelreadRel readRelvar showRelStrwriteRelvarBodywriteRelvarBody'NotEmptyRelabelrenameminus_countisEmptyassignupdateupdateA updateAll updateAllAdeleteP RelAssign RelAssignArg relAssignDyaOpDyaOpRes DyaOpLeft DyaOpRightMonOpMonOpResMonOpArgmonOprAggrAggUmember notMemberdyaOp⋈⋉⋊◅▻×⊠∣∪∩∖π≔⊂⊆⊃⊇#∈∉∋∌∅ colWidths allColWidths nColWidthsnextrestbuildOneColumn buildColumns buildColumns' mapListLenbuildRow buildRow' padTranspose'rPad hPadColumn hPadTable hRecFNames hRecQFNamesEmptyTS HFWRecSet showHListTypehListTypeToTRListparseHListTypetagFName tagQFName tagQFNameTS hRecQFNamesTSflatHRec flatHRec' buildHRecbuildHRecTypedbuildHRecTypedTSlistPresentReclistPresentTypedRecunwrap buildHRecSetbuildHRecSetTypedbuildHRecSetTypedTS$fFWPresent'HFWRecSetSet$fHFWPresentSet$fFWPresent'HFWRecRecord$fHFWPresentRecord%$fApplyABHPresentTypedRecAttrastringL $fApplyABHPresentRecAttrastringL$fFWPresent'HFWString[]$fFWPresent'HFWOthera $fFWPresenta$fHListTypeSynonymEmptyTS $fOrdDescRHdrconta_LKCPrTJwOTOLk4OU37YmeN Data.Set.BasefromList.=.$fHListTypeSynonymHaskRelTS relation'' unordRecEq' rElementType$fHTuple:Tagged$fHTuple:LabelreadRel'$fHFWPresentRelvar $fShowRelvarrhRenameLabelsIsEmptyjoinTOnR semiJoinTOnR tIntersectEq semiDiffTOnR projectTOnRrestrictByRTupleextendAByImage extendByImage unwrapUnary $fNotEmpty[] $fNotEmptyl$fApplyABRelabelTaggedb rewriteRelvar appendRelvar funSelfUpdateupdate'updateA'update'' updateAll' updateAllA' updateAll''doUpdate doUpdateAlldoDeletedyaOp'MonOp' MonOpArg'monOp'$fRelAssignRelvar $fRelAssignIO$fRelAssignSet$fDyaOpRelvarRelvar$fDyaOpIORelvar$fDyaOpRelvarIO$fDyaOpRelvarSet$fDyaOpSetRelvar $fDyaOpIOIO $fDyaOpSetIO $fDyaOpIOSet $fDyaOpSetSet$fMonOp'Relvar $fMonOp'IO $fMonOp'Set $fMonOpRelvar $fMonOpIO $fMonOpSet$fHFWPresentIObase Data.Proxy asProxyTypeOfProxyKProxyGHC.BasemconcatmappendmemptyMonoidtagge_6Tf46oJAk552xT73wGffBW Data.TaggedunTaggedTaggedData.HList.Variant zipVariant ZipVariantzipVR_ZipVRvariantToHMaybiedVariantToHMaybiedVariant unvariant' Unvariant' unvariant Unvariant splitVariant SplitVariantprojectVariantProjectVariantprojectExtendVariantProjectExtendVariant mkVarianthPrismHPrismHMaybiedToVariantFsHMapVextendsVariantExtendsVariantData.HList.TypeEqO TupleTypeIsKeyFNData.HList.TIPttipM TransTIPMttipTransTIP hUntagSelfhTagSelf TagUntagFDTagUntagTagRTIP HAllTaggedEqData.HList.TIC typeIndexed TypeIndexedticPrismTICData.HList.RecordUUnboxFsortForRecordUSSortForRecordUSRecordUToRecord RecordUSCxtRecordUSRecordURecordToRecordU hUpdateMany HUpdateMany HSubtract HMapUnboxFHLookupByHNatUS1HLookupByHNatUS HFindMany GetElemTy EqTagValueElemTyEqBoxF Unlabeled' UnlabeledunionSR' UnionSymRec'unionSR UnionSymRecUnLabelTaggedFnshowComponentsShowComponents relabeled Relabeled recordValues' RecordValuesR RecordValuesRecord rearranged Rearranged ReadComponentLabelsOfhLookupByLabelM1 HasFieldM1hLookupByLabelM HasFieldMhLookupByLabel' HasField'hLookupByLabelHasField hZipRecord hUnzipRecord HZipRecordhUpdateAtLabelHUpdateAtLabelHTPupdateAtLabel hRearrange4 HRearrange4 hRearrange3 HRearrange3 hRearrange2 HRearrange HRLabelSet HMemberLabel HMapTaggedFnHMapRHLensCxthLensHLens hLeftUnion HLeftUnion HLabelSet' HLabelSet HFindLabel hDeleteLabels HDeleteLabelsh2projectByLabels'H2ProjectByLabels'h2projectByLabelsH2ProjectByLabelsDuplicatedLabel DemoteMaybeData.HList.LabelabletoLabel projected Projected LabeledOptichLens' LabelableTy LabelableData.HList.Label3LblAddLabelData.HList.KeywordkwKwKErrUnexpectedKWErrReqdArgNotFoundData.HList.HZipSndHZipRHZipFhZip3HZip3hZip2hUnzip2MapSndMapFstHUnZipFstData.HList.HTypeIndexedHType2HNatCasehDeleteManyCaseHDeleteManyCaseData.HList.HSorthSortByHSortByHSortHSetByHSetHLeFnHIsSetByHIsSet HIsAscListHDownHAscListData.HList.HOccurs TypeNotFound TypeFound hOccursOpt' HOccursOpt' HOccursNot' hOccursMany' HOccursMany' hOccursMany HOccursManyhOccurs'HOccurs' hOccurrence' HOccurrence' hOccurrence HOccurrenceData.HList.HListPreludeSubTypehZipHZiphUnzipHUnzip HTypes2HNats HType2HNathProjectHProject HOccursNothOccursHOccursHExtendR.*.HExtend hDeleteMany HDeleteManyhDeleteAtLabelHDeleteAtLabelHAppendRhAppendHAppendData.HList.HListUncurryMappendUnHListtoHJustToHJustRToHJust ReadElementMapcarhZipList hUnzipListHZipListHUnfoldRhUnfold' HUnfoldFDHUnfold'HUnfoldhToTuple hFromTupleHTuplehTailsHTailsHTMemberhTIntersectBoolHTIntersectBool hTIntersect HTIntersect HStripPrefix hSplitAt1 HSplitAt1hSplitAtHSplitAthSplitHSplitTHSplitFHSplit hSpanEqBy2 HSpanEqBy2 hSpanEqBy1 HSpanEqBy1 hSpanEqBy HSpanEqBy hSequence HSequencehScanrHScanrHRmTaghReverseHReverseHRevAppRhRevAppHRevApp HReplicateR hReplicate HReplicateFD hReplicateF HReplicateF HReplicatehProxies HProxiesFDHProxies hPartitionEq1 HPartitionEq1 hPartitionEq HPartitionEqHNats HMemberP'HMemberP HMemberM2 HMemberM1HMemberMHMember'HMemberHMapTailHMapOutHMapLHMapCxtHMapConsHMapAuxHMaplist2HListSuffix hList2List HList2ListHNilHConsHList HLengthEq2 HLengthEq1 HLengthEqHLengthhIterateHIteratehInits1HInits1hInitsHInitshInitHInitRHInithGroupByHGroupBy HFromJusthFoldr1HFoldr1hFoldrHFoldrhFoldlHFoldlHFind2HFind1HFindHConcatR hConcatFD HConcatFDHConcathBuild'HBuild' HAppendListR HAppendList1 hAppendList HAppendList hAppendFD HAppendFDHAddTag fromHJust FromHJustR FromHJustFHCons2 DropProxy ConstMemptyAddProxyData.HList.HCurry hUncurry'hCurry'HCurry'Data.HList.HArrayKMember hUpdateAtHNatHUpdateAtHNatR HUpdateAtHNatHProjectByHNatsRHProjectByHNatsCtxHProjectAwayByHNatsRHProjectAwayByHNatsCtx hLookupByHNatHLookupByHNatR HLookupByHNat hDeleteAtHNatHDeleteAtHNatR HDeleteAtHNatFHUProjFHLookupByHNat ZipTagged TypeablePolyK showLabel ShowLabel SameLengths SameLength' sameLength SameLength SameLabelsLiftA2HUntagHShowHSeqHReadHPrintHOrHNothingHNotFDHNothNats2IntegralsHNats2IntegralsHNatEqHNat2Nat hNat2Integral HNat2IntegralHZeroHSuccHNatHLtHLeHJustHFmapHFlipHEqKHEqByFnHEqByHEqHDiv2hCondHCondHComphCast1HCast1hCastHCastHBoolEQHAnd HAllTaggedLVFunCxtFunAppFun'Fun FieldNotFoundFail ExtraFieldCompArityRevArityFwdArityapplyABApplyABapplyApplyRApplywitness untagSelfuntagunproxytagWithtagSelfretagreproxyproxyasTaggedTypeOfzipVR unvarianted' unvariantedsplitVariant1' splitVariant1 mkVariant1hMaybiedToVariants hMaybied'hMaybiedhMapVhMapOutV extendVariant castVariantData.HList.TIPtuple tipyTuple5 tipyTuple4 tipyTuple3 tipyTuple tipyUpdate tipyProject2 tipyProject tipyLens'tipyLens tipRecord' tipRecord tipHList'tipHListemptyTIP typeIndexed' ticVariant' ticVariant ticPrism'mkTIC1mkTIC'mkTIC unboxedS'unboxedSunboxed'unboxedhMapRUData.HList.RecordPunspun zipTagged unlabeled0 unlabeled' unlabeled relabeled' recordValues rearranged' newLVPairmkRecordlabelsOf labelLVPair hZipRecord2 hUnzipRecord2hTPupdateAtLabel hRenameLabel hRearrange' hRearrangehProjectByLabels2hProjectByLabels'hProjectByLabels hMapTaggedFnhMapR hListRecord' hListRecordhEndRhEndP emptyRecord asLabelsOf.@..<..<++..-..!.Data.HList.MakeLabels makeLabels6 makeLabels3 makeLabels makeLabelable projected'.==. nextLabel firstLabelrecToKW hTranspose hUpdateAt hTypes2HNats hType2HNathSplitBy hProjectBy hDeleteAthSort hOccursRest hOccursOpt hOccursMany1 hOccursFst emptyProxytoHJust2 listAsHList' listAsHList list2HListhUnfoldhTuple'hTuplehTailhTMemberhRmTag hReverse_hNatshMemberhMapOuthMapM_hMapMhMapLhMaphLengthhLasthHeadhFlaghEndhConcat hComposeListhBuildhAddTag fromHJust2hUncurryhCurryhCompose hSplitByHNatshProjectByHNats'hProjectByHNatshProjectAwayByHNats sameLabels labelToProxyhZerohTruehSucchPredhOrhNothLthLehFalsehEqhAnd asLengthOf