<div dir="ltr">Heh, defined the type predicate and never leveraged it. This is looking very promising.<div><div><br></div><div><div>#lang typed/racket</div><div><br></div><div>(provide</div><div> Even Even? +e)</div><div>
<br></div><div>(declare-refinement even?)</div><div>(define-type Even (Refinement even?))</div><div>(define-predicate Even? Even)</div><div><br></div><div style>;; Optimizer says this is in-lined away. Now I'm getting down right giddy.</div>
<div>(: An-Even (Number -> Even))</div><div>(define (An-Even num) </div><div> (assert num Even?))</div><div> </div><div>(: +e (Even Even -> Even))</div><div>(define (+e e1 e2)</div><div> (An-Even (+ e1 e2)))</div>
<div><br></div><div>(+e (An-Even 4) (An-Even 6))</div><div>(+e (An-Even 2) (An-Even 3))</div></div></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Dec 28, 2012 at 9:37 PM, Ray Racine <span dir="ltr"><<a href="mailto:ray.racine@gmail.com" target="_blank">ray.racine@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Can I just say, "DAMN YOU GUYS ARE GOOD." Thank you.<div><br></div><div><div>#lang typed/racket</div>
<div><br></div><div>(declare-refinement even?)</div><div><br></div><div>(define-type Even (Refinement even?))</div>
<div><br></div><div>(define-predicate Even? Even)</div><div><br></div><div>(: An-Even (Number -> Even))</div><div>(define (An-Even num) </div><div> (if (Even? num)</div><div> num</div><div> (error 'I-DONT-DO-ODD)))</div>
<div><br></div><div>(: +e (Even Even -> Even))</div><div>(define (+e e1 e2)</div><div> (let ((y (+ e1 e2)))</div><div> (if (Even? y)</div><div> y</div><div> (error 'RING-OF-EVEN-VIOLATION))))</div>
<div><br></div><div>(+e (An-Even 4) (An-Even 6))</div><div>(+e (An-Even 2) (An-Even 3))</div></div></div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Dec 28, 2012 at 9:01 PM, Ray Racine <span dir="ltr"><<a href="mailto:ray.racine@gmail.com" target="_blank">ray.racine@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Thanks, I'll poke around with TR's refinement types. If anyone has a reference detailing on the nature of TR's refinement types, please forward. In progress paper etc.</div>
<div><div><div class="gmail_extra">
<br><br><div class="gmail_quote">On Fri, Dec 28, 2012 at 8:53 PM, Carl Eastlund <span dir="ltr"><<a href="mailto:cce@ccs.neu.edu" target="_blank">cce@ccs.neu.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">What you describe sounds exactly like Typed Racket's refinement types. Statically typed languages like SML often incorporate refinements that can be determined entirely statically. TR allows arbitrary dynamic checks for its refinements, so it gets the "weak sort of Dependent Type" results you mention. So I wouldn't judge the capabilities of TR's refinement types based on papers about SML.<span><font color="#888888"><br>
</font></span><div class="gmail_extra"><span><font color="#888888"><br clear="all"><div>Carl Eastlund</div></font></span><div><div>
<br><div class="gmail_quote">On Fri, Dec 28, 2012 at 8:35 PM, Ray Racine <span dir="ltr"><<a href="mailto:ray.racine@gmail.com" target="_blank">ray.racine@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">Based on a google of a paper on refinement types in SML, no. Useful in their own right, and thanks for pointing out their (experimental) existence. I'm thinking more along the line of a quick win of a weak sort of Dependent Type for value types such as Number and String by leveraging existent Racket machinery.<div>
<br></div><div>See <a href="https://docs.google.com/document/d/10TQKgMiJTbVtkdRG53wsLYwWM2MkhtmdV25-NZvLLMA/edit" target="_blank">https://docs.google.com/document/d/10TQKgMiJTbVtkdRG53wsLYwWM2MkhtmdV25-NZvLLMA/edit</a> for a Scala endeavor along similar lines.</div>
<div><br></div><div>The goal is to support efficient generative, constrained, sub-types of primitive value types, specifically String and Number with minimal surgery to Racket.</div><div><br></div><div>Consider:</div><div>
<div><br></div><div>(define-value-type Age : Integer [1 .. 120])</div></div><div>(define-value-type SSN : String [1 .. 9 | 11] ssn-validation-predicate)</div><div><div><br></div><div>Goals:</div><div><br></div>
<div>1) Avoid boxing/unboxing (struct cell / cell-refs).</div><div>2) Create generative sub-types of certain base types, String, Number to satisfy TR. Note they are not opaque types but, i.e. T <: String </div>
<div><br></div><div>Item 2) means </div><div><br></div><div>;; works as Ages are Integers</div><div>(: add-ages (Age Age -> Age)) </div><div>(define (sum-ages a1 a2) </div><div>
(Age (+ a1 a2)) ;; + defined on Integers</div><div><br></div><div>;; not the same as (define-type Age Integer) because ...</div><div>(sum-ages 12 16) ;; fails as Integers are not Ages</div><div><br>
</div><div>;; lifting Integer to Age involves a runtime contract check, but no allocation.</div><div>(sum-ages (Age 12) (Age 16)) ;; fine, no allocation</div><div><br></div><div>A hand waved way of getting there, which got ugly quick and as I typed. I was sort of brainstorming if I could get Value Types without any Racket internal surgery and with no more than a bit of macrology.</div>
<div><br></div><div>So waving both hands wildly...</div><div><br></div><div>0) Modify the TR `cast' operator to recognize Value Type structures.</div><div> </div><div> a) The generated contract from the `cast' operator of a value type to an appropriate Value Type structure succeeds at runtime for an instance of the value type.</div>
<div> b) The generated contract from the `cast' operator applies the gen:validator on the Value Type structure as part of the contract.</div><div><br></div><div>1) Extend the struct: macro to allow a struct: parent to be not only another struct: but a [struct: | String | Number] </div>
<div><br></div><div> a) IF the parent is a struct: nothing new to do here.</div><div><br></div><div> b) If parent is a value type, String or a Number (value type)</div><div> - This is a Value Type structure.</div>
<div> - A value type structure has only one mandatory value which is of the same type as the parent.</div><div> - A Value Type structure is -sealed- and may not be used as the parent in another struct: definition.</div>
<div> - A Value Type structure's constructor is a (A -> A) pass-thru of the value. i.e., the struct: is never allocated to wrap the value.</div><div> - A Value Type structure _may_ have a gen:validate generic method associated with it.</div>
<div><br></div><div>4) To avoid creating a true Value Type structure instance via low level apis, they would need to be modified to prohibit creating any instance of a Value Type structure.</div><div><br>
</div><div>What we are trying to achieve is all of the type checking from TR using struct: to generate a new type at compile time, yet at runtime the instance values of the Value Type are the primitive values and are NOT manifested as the struct: instances.</div>
<div><br></div><div>Example: </div><div>Create an SSN Value Type.</div><div><br></div><div>;; An SSN is String value, of length 9 or 11, which is validated by a regular expression.</div><div>
<br></div><div>(: ssn-validation-predicate (String -> Boolean : SSN))</div><div>(define (ssn-validation-predicate str-ssn)</div><div> (regexp-match? ssn #rx(....)))</div><div><br></div><div>
(define-value-type SSN String [9 | 11] ssn-validation-predicate) ;; <br></div><div><br></div><div>The above roughly expands to:</div><div><br></div><div>(struct: SSN String ([value : String])</div>
<div> #:methods gen:validate ssn-validation-predicate)</div><div><br></div><div>(define SSN-validator-contract (and/contract ....)) ;;; combines string-length 9|11 check with ssn-validation-predicate into a contract</div>
<div><br></div><div>The struct: macro notes that this is a Value Type structure definition because its parent is a value type, String, and not another struct:. So the generated SSN constructor function avoids creating an actual structure at runtime and allows a string value as successfully cast to a SSN after applying any associated validation contract.</div>
<div><br></div><div>(: SSN (String -> SSN))</div><div>(define (SSN ssn) </div><div> (cast ssn SSN)) </div><div><br></div><div>In the above ...</div><div> - `cast' knows we are casting to a Value Type, SSN, so generated runtime contract allows a String value (and _not_ a SSN struct type instance) to be "passed-thru" but lifted to type SSN for TR purposes.</div>
<div> - Therefore, the cast fails an actual instance of an SSN structure, if one somehow managed to construct an instance.</div><div> - As part of the `cast' generated contract the SSN-validator-contract and length checks are combined and applied interstitial in the pass-thru of (String -> String).</div>
<div><br></div><div>And finally since SSN at runtime is a string value, at compile time it's a subtype of String so...</div><div><br></div><div>(substring (SSN "123-45-6789") 0 3) ;; works at TR compile time checking and at runtime running</div>
<div>(substring (SSN "123x456-5689") 0 3) ;; fails validation at runtime, though a sufficiently smart compiler would apply the contract validation check at compile time for values known at compile time.</div>
<div><br></div></div><div>Given:</div><div><br></div><div>(: parse-ssn (SSN -> (Listof String)))</div><div>(define (parse-ssn ssn)</div><div> (regexp-split ssn #rx"-"))</div>
<div><br></div><div>(parse-ssn "123-456-6789") ;; nope as strings are not SSNs</div><div>(parse-ssn (SSN "123-456-6789")) ;; works but runtime representation remained as a string value, no struct: box/unbox.</div>
<div><br></div><div><br></div><div><br></div><div><br></div></div><div><div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Dec 28, 2012 at 5:58 PM, Eric Dobson <span dir="ltr"><<a href="mailto:eric.n.dobson@gmail.com" target="_blank">eric.n.dobson@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Do refinement types work for what you want?<div><br></div><div><a href="http://docs.racket-lang.org/ts-reference/Experimental_Features.html?q=refinement#(form._((lib._typed-racket/base-env/prims..rkt)._declare-refinement))" target="_blank">http://docs.racket-lang.org/ts-reference/Experimental_Features.html?q=refinement#(form._((lib._typed-racket/base-env/prims..rkt)._declare-refinement))</a><br>
</div><div><br></div><div><div>#lang typed/racket</div><div>(declare-refinement even?)</div><div>(: two (Refinement even?))</div><div>(define two</div><div> (let ((x 2))</div><div> (if (even? x) x (error 'bad))))</div>
<div><br></div><div>There are a couple of issues with them, mostly that they are not sound when dealing with mutable objects or non pure functions, which allows you to break the soundness of TR.<br></div></div><div>
<a href="http://bugs.racket-lang.org/query/?cmd=view+audit-trail&pr=13059" target="_blank">http://bugs.racket-lang.org/query/?cmd=view+audit-trail&pr=13059</a></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">
<div><div>
On Fri, Dec 28, 2012 at 2:17 PM, Ray Racine <span dir="ltr"><<a href="mailto:ray.racine@gmail.com" target="_blank">ray.racine@gmail.com</a>></span> wrote:<br></div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div>
<div dir="ltr">Any plans something along the lines of Scala's proposed Value Types. <div><br></div><div><div>A path:</div><div><br></div><div>Allow for "special" struct: decls (vstruct: ?) where the parent is a limited subset of non structure parent types (base value types such as String, Number). </div>
<div><br></div><div>These special structures MUST contain one and only one value of the parent "special" type or it is a compile error. </div><div>The structure constructor does not construct the wrapping structure but instead passes through the wrapped value, but *always* invokes the validator during pass-thru.</div>
<div>TR treats the type as a subtype of the base value type.</div><div><br></div><div>e.g.</div><div><br></div><div>(struct: Identifier String ([value : String])</div><div> #:methods gen:validator (lambda (thing) ...) ;; is of type (Any -> Boolean : Identifier))</div>
<div><br></div><div>(define id (Identifier "myidentifier")) ;; validator invoked, no structure was allocated, `id' is just a String value, is a subtype of String.</div><div><br></div><div>
(define uc-id (Identifer (string-upcase id))) ;; validator invoked, as id is a just a string no unboxing in (string-upcase id), in fact no allocations here at all.</div><div><br></div><div>Under the covers the Identifier constructor never creates the structure, it acts as a pass through id : (String -> String) function. i.e. the runtime representation of `id' is always as a String so any struct <-> value boxing / unboxing is avoided. I think there is enough machinery in place to get pretty close to this.</div>
<div><br></div><div>What is gained?</div><div><br></div><div>What is done internally in TR defining Natural, Index, Exact-Positive-Integer can now be done without special internal defining, just another constrained base type. One can start to define things like Age [1 .. 120]. </div>
<div>Another is IMHO a HUGE source of program error is just not enough time to do proper validation at IO boundries where entering data is of the form Strings and Bytes and it needs to be lifted.</div><br>Consider the following typical use case from Amazon's AWS API, a Tasklist parameter.<br>
<br>Parameter - Tasklist : String[1-256]<br><br>Specifies the task list to poll for activity tasks.<br><br>The specified string must not start or end with whitespace. It must not contain a : (colon), / (slash), | (vertical bar), or any control characters (\u0000-\u001f | \u007f - \u009f). Also, it must not contain the literal string "arn".<br>
<br>Most likely, I'll punt.</div><div><br></div><div>(: call-it (String ... -> ...))</div><div>(define (call-it task-list ...)</div><div><br></div><div>If I'm ambitious today.</div><div>
<br></div><div>;; would prefer (define-new-type Tasklist String)</div><div>(define-type Tasklist String) ;; tighten things later down the road, <sigh> none type generative</div><div><br></div><div>
<div>(: call-it (Tasklist ... -> ...))</div><div>(define (call-it task-list ...)</div></div><div><br></div><div>What I'd like to do.</div><div><br></div><div>(define-value-type Tasklist String [1 .. 256] (lambda (this) ....)) ;; mad use of regexp in validator fn (Any -> Boolean : Tasklist)</div>
<div><br></div><div>(call-it (Tasklist "mytasklist") ...)</div><div><br></div><div>(call-it (Tasklist "arn:bad/tasklist") ...)</div><div><br></div><div>(define-value-type Age Integer [1 .. 120]) ;; no special validation beyond bounds check.</div>
<div><br></div><div> </div><div><br></div><div><br></div><div><br></div></div>
<br></div></div>____________________<br>
Racket Users list:<br>
<a href="http://lists.racket-lang.org/users" target="_blank">http://lists.racket-lang.org/users</a><br>
<br></blockquote></div><br></div>
</blockquote></div><br></div>
</div></div><br>____________________<br>
Racket Users list:<br>
<a href="http://lists.racket-lang.org/users" target="_blank">http://lists.racket-lang.org/users</a><br>
<br></blockquote></div><br></div></div></div></div>
</blockquote></div><br></div>
</div></div></blockquote></div><br></div>
</div></div></blockquote></div><br></div>