[{"data":1,"prerenderedAt":912},["ShallowReactive",2],{"content-\u002Freference\u002F0.4.x\u002Finformation-contracts":3,"use-cases-nav":905},{"id":4,"title":5,"author":6,"body":7,"date":6,"description":897,"extension":898,"meta":899,"navigation":169,"order":166,"path":900,"seo":901,"stem":902,"version":903,"__hash__":904},"content\u002Freference\u002F0.4.x\u002Finformation-contracts.md","Information Contracts",null,{"type":8,"value":9,"toc":885},"minimark",[10,22,29,35,43,60,80,85,88,224,235,240,243,280,284,287,350,356,360,368,471,481,492,496,499,538,558,562,568,637,643,653,657,663,666,718,721,738,755,759,762,851,854,873,881],[11,12,13,17,18,21],"p",{},[14,15,16],"em",{},"Finitio"," tries very hard not to be ",[14,19,20],{},"yet another data language",". In\nparticular, it aims at integrating as smoothly as possible with existing\ntechnologies, in particular with programming languages and data exchange\nformats (e.g. JSON or YAML).",[11,23,24,25,28],{},"This interoperability is handled through so-called ",[14,26,27],{},"information contracts",". In\nsome respect, information contracts are the dual of axiomatic contracts, i.e.\nthe dual of public behavioral APIs of software abstractions.",[11,30,31],{},[32,33],"img",{"alt":5,"src":34},"\u002Fimg\u002Fcontracts.png",[11,36,37,38,42],{},"For a given software abstraction, say a ",[39,40,41],"code",{},"Color",":",[44,45,46,54],"ul",{},[47,48,49,50,53],"li",{},"The ",[14,51,52],{},"axiomatic"," contract hides the internal representation in favor of a set\nof public behavioral methods to manipulate the abstraction (e.g. darkening\nand brightening the color),",[47,55,49,56,59],{},[14,57,58],{},"information"," contract hides the internal representation in favor of a\nset of public information representations of the abstraction (e.g. a RGB\ntriple, an hexadecimal string).",[11,61,62,63,66,67,70,71,73,74,79],{},"The data types involved in the definitions of the information contracts are\ncalled ",[14,64,65],{},"information types",", e.g. ",[39,68,69],{},"{r: Byte, g: Byte, b: Byte}"," (a tuple type).\n",[14,72,16],{}," provides a rich ",[75,76,78],"a",{"href":77},"\u002Freference\u002F0.4.x\u002Ftype-system","type system","\ndedicated at capturing those data types precisely, mostly because type systems\nof mainstream programming languages fail at providing good support for them.",[81,82,84],"h2",{"id":83},"dressing-undressing","Dressing & Undressing",[11,86,87],{},"In a more precise way, an information contract is actually a set of\nfunction pairs, such as:",[89,90,95],"pre",{"className":91,"code":92,"language":93,"meta":94,"style":94},"language-finitio shiki shiki-themes github-light github-dark","# RGB information contract\ndress   :: {r: Byte, g:Byte, b: Byte} -> Color\nundress :: Color -> {r: Byte, g:Byte, b: Byte}\n\n# HEX information contract\ndress   :: String( s | ... ) -> Color\nundress :: Color -> String( s | ... )\n","finitio","",[39,96,97,106,137,164,171,177,203],{"__ignoreMap":94},[98,99,102],"span",{"class":100,"line":101},"line",1,[98,103,105],{"class":104},"sJ8bj","# RGB information contract\n",[98,107,109,113,117,120,122,125,127,130,134],{"class":100,"line":108},2,[98,110,112],{"class":111},"sVt8B","dress   :: {r: ",[98,114,116],{"class":115},"sj4cs","Byte",[98,118,119],{"class":111},", g:",[98,121,116],{"class":115},[98,123,124],{"class":111},", b: ",[98,126,116],{"class":115},[98,128,129],{"class":111},"} ",[98,131,133],{"class":132},"szBVR","->",[98,135,136],{"class":115}," Color\n",[98,138,140,143,145,148,151,153,155,157,159,161],{"class":100,"line":139},3,[98,141,142],{"class":111},"undress :: ",[98,144,41],{"class":115},[98,146,147],{"class":132}," ->",[98,149,150],{"class":111}," {r: ",[98,152,116],{"class":115},[98,154,119],{"class":111},[98,156,116],{"class":115},[98,158,124],{"class":111},[98,160,116],{"class":115},[98,162,163],{"class":111},"}\n",[98,165,167],{"class":100,"line":166},4,[98,168,170],{"emptyLinePlaceholder":169},true,"\n",[98,172,174],{"class":100,"line":173},5,[98,175,176],{"class":104},"# HEX information contract\n",[98,178,180,183,186,189,193,196,199,201],{"class":100,"line":179},6,[98,181,182],{"class":111},"dress   :: ",[98,184,185],{"class":115},"String",[98,187,188],{"class":111},"( ",[98,190,192],{"class":191},"s4XuR","s",[98,194,195],{"class":132}," |",[98,197,198],{"class":111}," ... ) ",[98,200,133],{"class":132},[98,202,136],{"class":115},[98,204,206,208,210,212,215,217,219,221],{"class":100,"line":205},7,[98,207,142],{"class":111},[98,209,41],{"class":115},[98,211,147],{"class":132},[98,213,214],{"class":115}," String",[98,216,188],{"class":111},[98,218,192],{"class":191},[98,220,195],{"class":132},[98,222,223],{"class":111}," ... )\n",[11,225,226,227,230,231,234],{},"In other words, each public data representation of an abstraction comes\nwith two (pure) functions that allow ",[14,228,229],{},"dressing"," the corresponding\ninformation type with the abstraction behavior, and ",[14,232,233],{},"undressing"," the latter\nthe other way round.",[236,237,239],"h3",{"id":238},"validating-is-not-dressing","Validating is not dressing",[11,241,242],{},"The two are easy to confuse, and both implementations expose them separately.",[44,244,245,252,274],{},[47,246,247,251],{},[248,249,250],"strong",{},"Validating"," asks whether a value already belongs to a type. It is a\nquestion about set membership, and nothing is converted.",[47,253,254,257,258,261,262,265,266,269,270,273],{},[248,255,256],{},"Dressing"," converts an exchange-level document into host values. A\ndocument may well ",[14,259,260],{},"fail"," validation and still dress successfully — that is\nthe entire point of a contract. The string ",[39,263,264],{},"\"2014-07-17\""," is not a ",[39,267,268],{},"Date",";\nit is the ISO 8601 ",[14,271,272],{},"representation"," of one, and dressing is what turns it\ninto a real date.",[47,275,276,279],{},[248,277,278],{},"Undressing"," goes the other way, producing something you can serialise\nagain.",[81,281,283],{"id":282},"declaring-an-abstract-data-type","Declaring an abstract data type",[11,285,286],{},"A type can list its representations, each introduced by a name in angle\nbrackets:",[89,288,290],{"className":91,"code":289,"language":93,"meta":94,"style":94},"Color = \u003Crgb> {r: Byte, g: Byte, b: Byte},\n        \u003Chex> String( s | \u002F^#[0-9a-f]{6}$\u002Fi.test(s) )\n",[39,291,292,324],{"__ignoreMap":94},[98,293,294,297,300,303,307,310,312,315,317,319,321],{"class":100,"line":101},[98,295,41],{"class":296},"sScJk",[98,298,299],{"class":132}," =",[98,301,302],{"class":111}," \u003C",[98,304,306],{"class":305},"s9eBZ","rgb",[98,308,309],{"class":111},"> {r: ",[98,311,116],{"class":115},[98,313,314],{"class":111},", g: ",[98,316,116],{"class":115},[98,318,124],{"class":111},[98,320,116],{"class":115},[98,322,323],{"class":111},"},\n",[98,325,326,329,332,335,337,339,341,343,347],{"class":100,"line":108},[98,327,328],{"class":111},"        \u003C",[98,330,331],{"class":305},"hex",[98,333,334],{"class":111},"> ",[98,336,185],{"class":115},[98,338,188],{"class":111},[98,340,192],{"class":191},[98,342,195],{"class":132},[98,344,346],{"class":345},"sA_wV"," \u002F^#[0-9a-f]{6}$\u002Fi",[98,348,349],{"class":111},".test(s) )\n",[11,351,352,353,355],{},"Defined like that, ",[39,354,41],{}," behaves as a union type: it lets valid RGB triples\nand hexadecimal strings pass, unchanged.",[81,357,359],{"id":358},"binding-to-the-host-language","Binding to the host language",[11,361,362,363,367],{},"Representations become contracts once they are connected to an abstraction of\nthe host language. The type then starts with a\n",[75,364,366],{"href":365},"\u002Freference\u002F0.4.x\u002Ftype-system#builtin-types","builtin type"," naming that\nabstraction, and each representation may carry its dress and undress\nfunctions, written with a leading backslash:",[89,369,371],{"className":91,"code":370,"language":93,"meta":94,"style":94},"Color = .Color\n  \u003Crgb> [Integer] \\( triple | Color.rgb(triple) )\n                  \\( color  | color.toRgb()     )\n  \u003Chex> String    \\( hex    | Color.hex(hex)    )\n                  \\( color  | color.toHex()     )\n",[39,372,373,385,417,433,458],{"__ignoreMap":94},[98,374,375,377,379,382],{"class":100,"line":101},[98,376,41],{"class":296},[98,378,299],{"class":132},[98,380,381],{"class":111}," .",[98,383,384],{"class":115},"Color\n",[98,386,387,390,392,395,398,401,404,406,409,411,414],{"class":100,"line":108},[98,388,389],{"class":111},"  \u003C",[98,391,306],{"class":305},[98,393,394],{"class":111},"> [",[98,396,397],{"class":115},"Integer",[98,399,400],{"class":111},"] ",[98,402,403],{"class":132},"\\",[98,405,188],{"class":111},[98,407,408],{"class":191},"triple",[98,410,195],{"class":132},[98,412,413],{"class":115}," Color",[98,415,416],{"class":111},".rgb(triple) )\n",[98,418,419,422,424,427,430],{"class":100,"line":139},[98,420,421],{"class":132},"                  \\",[98,423,188],{"class":111},[98,425,426],{"class":191},"color",[98,428,429],{"class":132},"  |",[98,431,432],{"class":111}," color.toRgb()     )\n",[98,434,435,437,439,441,443,446,448,450,453,455],{"class":100,"line":166},[98,436,389],{"class":111},[98,438,331],{"class":305},[98,440,334],{"class":111},[98,442,185],{"class":115},[98,444,445],{"class":132},"    \\",[98,447,188],{"class":111},[98,449,331],{"class":191},[98,451,452],{"class":132},"    |",[98,454,413],{"class":115},[98,456,457],{"class":111},".hex(hex)    )\n",[98,459,460,462,464,466,468],{"class":100,"line":173},[98,461,421],{"class":132},[98,463,188],{"class":111},[98,465,426],{"class":191},[98,467,429],{"class":132},[98,469,470],{"class":111}," color.toHex()     )\n",[11,472,473,474,477,478,480],{},"Dressing ",[39,475,476],{},"[12, 13, 14]"," against that type now yields an actual ",[39,479,41],{},"\ninstance rather than an array.",[11,482,483,484,487,488,491],{},"The functions are host-language code, so this part of a schema is inherently\nimplementation-specific — the example above is JavaScript, and the Ruby\nequivalent would call ",[39,485,486],{},"Color.rgb(triple)"," and ",[39,489,490],{},"color.to_rgb",".",[236,493,495],{"id":494},"conventions-instead-of-explicit-functions","Conventions instead of explicit functions",[11,497,498],{},"Both implementations can infer the two functions from naming conventions, so\nthe explicit form is often unnecessary:",[89,500,502],{"className":91,"code":501,"language":93,"meta":94,"style":94},"Color = .Color\n  \u003Crgb> [Integer]\n  \u003Chex> String\n",[39,503,504,514,527],{"__ignoreMap":94},[98,505,506,508,510,512],{"class":100,"line":101},[98,507,41],{"class":296},[98,509,299],{"class":132},[98,511,381],{"class":111},[98,513,384],{"class":115},[98,515,516,518,520,522,524],{"class":100,"line":108},[98,517,389],{"class":111},[98,519,306],{"class":305},[98,521,394],{"class":111},[98,523,397],{"class":115},[98,525,526],{"class":111},"]\n",[98,528,529,531,533,535],{"class":100,"line":139},[98,530,389],{"class":111},[98,532,331],{"class":305},[98,534,334],{"class":111},[98,536,537],{"class":115},"String\n",[11,539,540,541,543,544,547,548,551,552,554,555,557],{},"Here ",[39,542,306],{}," dresses through ",[39,545,546],{},"Color.rgb(...)"," and undresses through\n",[39,549,550],{},"color.toRgb()"," in JavaScript (",[39,553,490],{}," in Ruby). The standard\nlibrary's ",[39,556,268],{}," is defined exactly this way.",[81,559,561],{"id":560},"choosing-what-to-undress-to","Choosing what to undress to",[11,563,564,565,42],{},"Dressing picks a representation by trying them in order. Undressing has to be\ntold which one to target, since an abstraction usually has several. A schema\ndoes that by naming a type built from a specific contract, with\n",[39,566,567],{},"Type\u002Fcontract",[89,569,571],{"className":91,"code":570,"language":93,"meta":94,"style":94},"Color = .Color\n  \u003Crgb> [Integer]\n  \u003Chex> String\n\nOutput = [Color\u002Fhex]\n\n[Color]\n",[39,572,573,583,595,605,609,624,628],{"__ignoreMap":94},[98,574,575,577,579,581],{"class":100,"line":101},[98,576,41],{"class":296},[98,578,299],{"class":132},[98,580,381],{"class":111},[98,582,384],{"class":115},[98,584,585,587,589,591,593],{"class":100,"line":108},[98,586,389],{"class":111},[98,588,306],{"class":305},[98,590,394],{"class":111},[98,592,397],{"class":115},[98,594,526],{"class":111},[98,596,597,599,601,603],{"class":100,"line":139},[98,598,389],{"class":111},[98,600,331],{"class":305},[98,602,334],{"class":111},[98,604,537],{"class":115},[98,606,607],{"class":100,"line":166},[98,608,170],{"emptyLinePlaceholder":169},[98,610,611,614,616,619,621],{"class":100,"line":173},[98,612,613],{"class":296},"Output",[98,615,299],{"class":132},[98,617,618],{"class":111}," [",[98,620,41],{"class":115},[98,622,623],{"class":111},"\u002Fhex]\n",[98,625,626],{"class":100,"line":179},[98,627,170],{"emptyLinePlaceholder":169},[98,629,630,633,635],{"class":100,"line":205},[98,631,632],{"class":111},"[",[98,634,41],{"class":115},[98,636,526],{"class":111},[11,638,639,640,642],{},"Reading that: the main type dresses a sequence of colors from whatever\nrepresentation each one uses, and ",[39,641,613],{}," describes the form to undress them\nback into — hexadecimal strings.",[644,645,647],"impl-note",{"impl":646},"finitio.js",[11,648,649,650,652],{},"finitio-rb does not parse the ",[39,651,567],{}," form, so selecting a target\nrepresentation this way is a finitio.js capability today rather than a\nportable language feature.",[81,654,656],{"id":655},"contracts-in-action","Contracts in action",[11,658,659,660,662],{},"Dressing and undressing generally apply recursively, e.g. when involving\ncollection and abstract data types. This provides the real ability of\n",[14,661,16],{}," to dress and undress complex data involving many information\ncontracts and many abstractions.",[11,664,665],{},"Consider the following Finitio system, for dressing sequences of tuples\nhaving a name attribute restricted to simple words:",[89,667,669],{"className":91,"code":668,"language":93,"meta":94,"style":94},"@import finitio\u002Fdata\n\nWord = String( s | \u002F^[a-z]+$\u002F.test(s) )\n\n[{ name: Word }]\n",[39,670,671,680,684,704,708],{"__ignoreMap":94},[98,672,673,676],{"class":100,"line":101},[98,674,675],{"class":132},"@import",[98,677,679],{"class":678},"sZZnC"," finitio\u002Fdata\n",[98,681,682],{"class":100,"line":108},[98,683,170],{"emptyLinePlaceholder":169},[98,685,686,689,691,693,695,697,699,702],{"class":100,"line":139},[98,687,688],{"class":296},"Word",[98,690,299],{"class":132},[98,692,214],{"class":115},[98,694,188],{"class":111},[98,696,192],{"class":191},[98,698,195],{"class":132},[98,700,701],{"class":345}," \u002F^[a-z]+$\u002F",[98,703,349],{"class":111},[98,705,706],{"class":100,"line":166},[98,707,170],{"emptyLinePlaceholder":169},[98,709,710,713,715],{"class":100,"line":173},[98,711,712],{"class":111},"[{ name: ",[98,714,688],{"class":115},[98,716,717],{"class":111}," }]\n",[11,719,720],{},"Dressing a JSON document involves a chain of contracts:",[44,722,723,726,732,735],{},[47,724,725],{},"Dressing a JSON string into a host string (by the JSON parser)",[47,727,728,729,731],{},"Dressing that string into a ",[39,730,688],{}," (by Finitio)",[47,733,734],{},"Dressing a host hash\u002Fobject into a tuple (by Finitio)",[47,736,737],{},"Dressing a host array into a sequence (by Finitio)",[11,739,740,741,744,745,747,748,751,752,754],{},"The concrete dressing result is implementation-dependent, as it involves the\ndefinition of the representation function ",[39,742,743],{},"Rep"," that binds Finitio types to\ntypes in the host language. The aim is not to define new host abstractions,\ne.g. classes, for every ",[14,746,16],{}," type defined in a system but rather to check\nthat values ",[14,749,750],{},"conform"," to ",[14,753,16],{}," types and choose an idiomatic\nrepresentation in the host language. However, all those information contracts\nare actually involved in the dressing process and provide as many places to\nvalidate and coerce data in practice.",[81,756,758],{"id":757},"data-interoperability","Data interoperability",[11,760,761],{},"According to the host programming language, the interoperability with exchange\nformats such as JSON is more or less complete. In Ruby, for instance, the\ninteroperability is already pretty good, and can be explained as follows:",[89,763,765],{"className":91,"code":764,"language":93,"meta":94,"style":94},"RbBoolean = \u003Cjson> JSBoolean\nRbString  = \u003Cjson> JSString\nRbNumeric = \u003Cjson> JSNumber\nRbHash    = \u003Cjson> JSObject\nRbArray   = \u003Cjson> JSArray\n",[39,766,767,784,801,817,834],{"__ignoreMap":94},[98,768,769,772,774,776,779,781],{"class":100,"line":101},[98,770,771],{"class":296},"RbBoolean",[98,773,299],{"class":132},[98,775,302],{"class":111},[98,777,778],{"class":305},"json",[98,780,334],{"class":111},[98,782,783],{"class":115},"JSBoolean\n",[98,785,786,789,792,794,796,798],{"class":100,"line":108},[98,787,788],{"class":296},"RbString",[98,790,791],{"class":132},"  =",[98,793,302],{"class":111},[98,795,778],{"class":305},[98,797,334],{"class":111},[98,799,800],{"class":115},"JSString\n",[98,802,803,806,808,810,812,814],{"class":100,"line":139},[98,804,805],{"class":296},"RbNumeric",[98,807,299],{"class":132},[98,809,302],{"class":111},[98,811,778],{"class":305},[98,813,334],{"class":111},[98,815,816],{"class":115},"JSNumber\n",[98,818,819,822,825,827,829,831],{"class":100,"line":166},[98,820,821],{"class":296},"RbHash",[98,823,824],{"class":132},"    =",[98,826,302],{"class":111},[98,828,778],{"class":305},[98,830,334],{"class":111},[98,832,833],{"class":115},"JSObject\n",[98,835,836,839,842,844,846,848],{"class":100,"line":173},[98,837,838],{"class":296},"RbArray",[98,840,841],{"class":132},"   =",[98,843,302],{"class":111},[98,845,778],{"class":305},[98,847,334],{"class":111},[98,849,850],{"class":115},"JSArray\n",[11,852,853],{},"Such a mechanism is already built into the Ruby standard library, and explains\nwhy working with JSON data is rather natural in Ruby. Among others, this\nallows Finitio-rb to be kept simple, and work with the Ruby type system only,\ndelegating the interoperability with JSON to the usual parsing library.",[11,855,856,857,859,860,862,863,487,865,868,869,491],{},"Observe, however, that this interoperability is straightforward but actually\nbiased towards JSON. The developer has no way of stating that some value must\nbe a Ruby ",[39,858,397],{},", since the JSON specification does not distinguish\nintegers from reals. One aim with ",[14,861,16],{}," is to give developers a way to fix\nthis, by specifying more specific information contracts and having full\ncontrol of them — which is also why ",[39,864,397],{},[39,866,867],{},"Real"," are the two standard\nlibrary types whose behaviour differs most between bindings. See\n",[75,870,872],{"href":871},"\u002Freference\u002F0.4.x\u002Fstdlib#numbers-need-care","numbers need care",[11,874,875,876,880],{},"You can watch all of this happen, step by step, on the\n",[75,877,879],{"href":878},"\u002Ftry","playground",": every example reports validating, dressing and undressing\nseparately.",[882,883,884],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":94,"searchDepth":139,"depth":139,"links":886},[887,890,891,894,895,896],{"id":83,"depth":108,"text":84,"children":888},[889],{"id":238,"depth":139,"text":239},{"id":282,"depth":108,"text":283},{"id":358,"depth":108,"text":359,"children":892},[893],{"id":494,"depth":139,"text":495},{"id":560,"depth":108,"text":561},{"id":655,"depth":108,"text":656},{"id":757,"depth":108,"text":758},"Finitio 0.4 information contracts: dressing and undressing data.","md",{},"\u002Freference\u002F0.4.x\u002Finformation-contracts",{"title":5,"description":897},"reference\u002F0.4.x\u002Finformation-contracts","0.4.x","J3PUtEfrOD6a4W7WvEUHiug1mMsp4BX1PM3kjTWUwNg",[906,909],{"path":907,"title":908},"\u002Fuse-cases","Use cases",{"path":910,"title":911},"\u002Fuse-cases\u002Fcleaning-csv-files","Cleaning .csv files",1786026251331]