You are on page 1of 14

# 3/29/2014

A Neighborhood of Infinity: Constraining Types with Regular Expressions
Share

0   More

Next Blog»

dpiponi@gmail.com   New Post   Design   Sign Out

A Neighborhood of Infinity
Saturday, August 14, 2010

Constraining Types with Regular Expressions
Structures with Constraints Here's a picture of an element of a binary tree type:

The leaves correspond to elements and the letters indicate the type of those elements. If we read the leaves from left to right they match the regular expression A*B*. We can define a binary type whose leaves always match this string by making use of dissections. Similarly we can construct all kinds of other structures, such as lists and more general trees, whose leaves match this expression. But can we make structures that match other regular expressions? Here are some examples: 1. Structures that match the expression An for some fixed n. These are structures, like trees, that have a fixed size. For the case n=5 we need a type that includes elements like:

http://blog.sigfpe.com/2010/08/constraining-types-with-regular.html

1/14

5. It's a 'hole'. Many kinds of other constraints you could imagine on a datastructure. But it's more complex for trees because subtrees may have odd or even size even though the overall structure is even in size. or whose sequence of leaves never contain a certain subsequence. We could also generalise to ((AA)n)* for some fixed n.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions 2. We can also construct a variety of types like those matching A*BA*CB* by using a mixture of dissection and differentiation: 4. Structures that match (AA)*.sigfpe.com/2010/08/constraining-types-with-regular. We can just use a list of pairs. A bytestring.html 2/14 . http://blog. These are structures like trees with an even number of elements. Structures that match A*1A*.or rope-like type used to represent a string that is statically guaranteed to match a given regular expression. Here's an example: The element marked 1 is of unit type ( ) . So this regular expression is a derivative type. 3. Like trees which are guaranteed not to have two neighbouring leaves of the same type. This is easy for lists.

The element could correspond http://blog.U n d e c i d a b l e I n s t a n c e s .E m p t y D a t a D e c l s . the language) matching a regular expression is precisely the language accepted by a finite state automaton.sigfpe. This is the language we get by considering all paths from state 0 to state 1 in the following automaton: Note that I have overloaded the numeral '1' to mean both the type with one element (when labelling an edge) and to mean the state numbered 1 (when labelling a vertex).com/2010/08/constraining-types-with-regular. A list in Lij(x) is either an empty list.html 3/14 . We can define a list type through d a t aL i s tx=N i l|C o n sx( L i s tx ) Algebraically we can write this as l(x) = 1+x l(x) Let's try approaching the derivative of a list from the perspective of regular expressions. the combination has to match a possible path from i to j. or constructed from a first element and a list. Define the Lij(x) to be the type of lists whose sequence of elements correspond to all possible paths from state i to state j. If we combine an element and a list. Let's consider the case x*1x*. We know from Kleene's Theorem that the set of strings (ie. There are a number of ways we could do this.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions So the challenge is this: can we implement a uniform way of building any container type to match any regular expression we want? We'll need some extensions: >{ #O P T I O N S _ G H Cf w a r n i n c o m p l e t e p a t t e r n s# } >{ #L A N G U A G ET y p e F a m i l i e s .T y p e O p e r a t o r s# } First Example: Differentiating Lists Let l(x) be the type of lists of elements of type x.

As N i ltakes no arguments. So the matrix for such paths is: I = (1 0) (0 1) Let's also define the matrix L(x)= (Lij(x)). We can apply this rule to any container type. the order of multiplication matters. We can rewrite this using standard matrix notation: L(x) = I + X(x)L(x) Compare with the definition of ordinary lists given above. And we replace the structure we're trying to define with a family of types . This isn't quite the usual semiring of types. The words above boil down to: Lij(x) = Iij+Σk Xik(x)Lkj(x) where the sum is over all the places we might visit on the first step of the journey from i to j. For one thing. We can describe this replacement more formally: it's a homomorphism from the set of types to the set of matrices of types. We get the type of constrained lists by taking the original definition of a list and replacing everything with matrices. We replace 1 with I.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions to a transition from i to k. (Actually. suppose we wish to repeat the above for trees. t(x). This is Xij(x) where X(x) = (Xij(x)) = (x 1) (0 x) On the other hand. we have t(x) = x+t(x)2 http://blog.com/2010/08/constraining-types-with-regular. So we must replace C o n swith something that for its first argument takes a type corresponding to a single automaton step from i to k. then the remainder of the list must correspond to a path from k to j.html 4/14 . it must correspond to paths of length zero in the automaton.j)-th element of the transition matrix for the automaton. The set of paths from i to j that correspond to a single step is the (i.sigfpe.) And it doesn't just apply to lists. But if this is the case. Then we know that for ordinary binary trees. For example. we also need to replace N i lwith a version that respects transitions too. it's a bit more subtle than that. For its second argument it must take an element of Lkj(x).one for each pair of start and end states for the automaton. The only such paths are zero length paths from a state to itself. We replace x with the transition matrix of the automaton.

So we have (st(x) (st)'(x)) = (s(x) s'(x)) (t(x) t'(x)) (0 st(x)) (0 s(x)) (0 t(x)) Multiply out and we find that (st)'(x) = s(x)t'(x)+s'(x)t(x) In other words.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions We replace this with T(x) = X(x)+T(x)2 We started this section by considering the specific case of the pattern x*1x* with a corresponding matrix X(x). the usual Leibniz rule for differentiation is nothing more than a statement about transitions for the automaton I drew above.html 5/14 . ('K' is an abbreviation for 'constant' matrix. Because X10=0 and X00=X11 it's not hard to see that any type T we constrain using this regular expression will also have similar 'shape'.sigfpe. ie. A Specification If you haven't checked Brent Yorgey's solution to my problem last week. ( ) ) > ( V o i d . now is a good opportunity. T10=0 and T00=T11. The goal is to be able to define a transition matrix like this. The best thing now is probably to put together some code to see how this all looks. much of what I said above applies for any finite state automaton whose edges are labelled by types. So we can write T = (t(x) t'(x)) (0 t(x)) where by definition. (Sij) and (Tij).com/2010/08/constraining-types-with-regular. Suppose we have two such collections of types.a) And then define a functor like this: http://blog. t(x)=T00(x) and t'(x)=T01(x). To get a transition 0→1 you either go 0→0→1 or 0→1→1.) >t y p eDa=K 2 2( a . Although I talked specifically about differentiation. Then the leaves of the pair structure correspond to a path from i to k. My code is a generalisation of that but it may be helpful to look at Brent's specialisation first. Now consider the types of pairs where the first element is of type Sij and the second of Tjk.

ordinary lists should appear as the element at position (0.I 1 )( Exyz )T r e e F to define the second divided difference of trees. The Implementation We'll need a type with no elements: >d a t aV o i d And some type level integers: http://blog.I 0 )( Dx )L i s t F I'm using I nto represent the integer n at the type level. We'd like then to able to form the matrix of fixed points of Y = L i s t FXY .I 0 )( DI n t )T r e e F >t y p eT r e e '=F i x( I 0 .html 6/14 .y .V o i d .com/2010/08/constraining-types-with-regular.I 1 )( DI n t )T r e e F And of course it needs to work with other transition matrices. Derivatives of lists should appear at (0.sigfpe.0) in the matrix: >t y p eL i s tx=F i x( I 0 .z ) So we'd expect >t y p eD D T r e exyz=F i x( I 0 .3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions >t y p eL i s t F=I: + :( X: * :Y ) Think of L i s t Fbeing a bifunctor taking arguments Xand Y . ( ) ) > ( V o i d . ( ) . For example x*1y*1z* has the following transition diagram and matrix: >t y p eExyz=K 3 3( x . V o i d ) > ( V o i d . So it needs to be defined in a way that also works for trees: >t y p eT r e e F=X: + :( Y: * :Y ) >t y p eT r e e =F i x( I 0 . In this case.I 1 )( Dx )L i s t F But F i xis intended to be completely generic.1) so we want >t y p eL i s t 'x=F i x( I 0 .

) We can now define matrix addition through pointwise addition of the elements: >d a t a( : + )mn >t y p ei n s t a n c eD i m( m: +n )=D i mm >t y p ei n s t a n c e( m: +n ): !i j=E i t h e r( m: !i j )( n: !i j ) And similarly we can define multiplication. I'm using the type-level function P r o d u c t 'to perform the loop required in the definition of matrix multiplication: >d a t a( : * )mn >t y p ei n s t a n c eD i m( m: *n )=D i mm >t y p ei n s t a n c e( m: *n ): !i j=P r o d u c t 'I 0( D i mm )mn: !i j >d a t aP r o d u c t 'ikmn >t y p ei n s t a n c eP r o d u c t 'pI 1mn: !( i .j ) ) > ( P r o d u c t '( Sp )( Sc )mn: !( i .j ) ) >t y p ei n s t a n c eP r o d u c t 'p( S( Sc ) )mn: !( i .sigfpe.com/2010/08/constraining-types-with-regular.j )=E i t h e r > ( m: !( i .) To turn these into usable types we need to implement the homomorphism I http://blog.p ) .n: !( p .p ) . j)-th element: >t y p ef a m i l yD i mx: :* >t y p ef a m i l y( : ! )xi j: :* (Thanks to Brent's tutorial that code is much better than how it used to look. we need a type-level function to give its dimension and another to access its (i.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions >d a t aZ e r o >d a t aSa >t y p eI 0=Z e r o >t y p eI 1=SI 0 >t y p eI 2=SI 1 >t y p eI 3=SI 2 Now we'll need some type-level matrices.) Now we need some types to represent our functors: >d a t aI >d a t aX >d a t aY >d a t aKn >d a t a( f: + :g ) >d a t a( f: * :g ) >d a t aFmf (I think phantom empty data types should be called ethereal types. For any square matrix.html 7/14 .j ) ) > (Weird seeing all that familiar matrix multiplication code at the type level.n: !( p .j )=( m: !( i .

We could (as Brent suggests) write some smart constructors to make our life easier. The L e f tand R i g h tget a bit tedious to write. But for now I'm writing everything explicitly so you can see what's going on: >x 0=F i x( L e f t( ) ) : :L i s tI n t >x 1=F i x( R i g h t( L e f t( 1 . we can think of it as xI+Delta where Delta is the matrix http://blog.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions described above.a s ) ) ) )=a:i s o 1 'a s >i s o 1 '( F i x( R i g h t( R i g h ta ) ) ) =e r r o r" C a n ' tb ec a l l e da sai sv o i d " So that's it! If we can write our container as the fixed point of a polynmomial functor. and if we can convert our regular expression to a finite state automaton. Take a look at the definition of D xagain. So here are the rules laid out formally: >t y p eI d=K 2 2( ( ) . What have we learnt? We haven't just solved the original problem.( ) ) >t y p ef a m i l y H o ms e l fmf: :* >t y p ei n s t a n c eH o ms e l fmI=I d >t y p ei n s t a n c eH o ms e l fmX=m >t y p ei n s t a n c eH o ms e l fmY=s e l f >t y p ei n s t a n c eH o ms e l fm( Kn )=n >t y p ei n s t a n c eH o ms e l fm( f: + :g )=H o ms e l fmf: +H o ms e l fmg >t y p ei n s t a n c eH o ms e l fm( f: * :g )=H o ms e l fmf: *H o ms e l fmg >t y p ei n s t a n c eD i m( Fmf )=D i mm >d a t aF i xi jmf=F i x( H o m( Fmf )mf: !i j ) >t y p ei n s t a n c e( : ! )( Fmf )i j=F i xi jmf That's more or less it. We've shown that derivatives and dissections are special cases of a more general operation. V o i d ) > ( V o i d . We can now go ahead and try to construct some elements.F i x( L e f t( ) ) ) ) ): :L i s tI n t x 0is the empty list.sigfpe.html 8/14 . then F i xcompletely automatically builds the constrained container type. x 1is the list [ 1 ] . We can explicitly implement the isomorphism with the more familiar list type: >i s o 1: :[ x ]>L i s tx >i s o 1[ ] =F i x( L e f t( ) ) >i s o 1( a : a s )=F i x( R i g h t( L e f t( a .i s o 1a s ) ) ) >i s o 1 ': :L i s tx>[ x ] >i s o 1 '( F i x( L e f t( ) ) ) =[ ] >i s o 1 '( F i x( R i g h t( L e f t( a . But this is intended as a proof that the concept works rather than a user-friendly API.com/2010/08/constraining-types-with-regular.

3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions >t y p eD e l t a=K 2 2( V o i d . By the way. I suspect 2-categories give a language to talk about these things. I think that for any automaton we have a 2-category. >d a t aK 2 2r o w 0r o w 1 >t y p ei n s t a n c eD i m( K 2 2r o w 0r o w 1 )=I 2 > >t y p ei n s t a n c e( : ! )( K 2 2( m 0 0 .. As things stand.xn-1) defined there is nothing other than a transition matrix for this automaton: In fact. The 2-category structure is probably important. I haven't worked out the details however..I 1 )=m 0 1 >t y p ei n s t a n c e( : ! )( K 2 2r o w 0( m 1 0 . The 0-cells are states.I 1 )=m 1 1 http://blog. Leftover bits of code K 2 2and K 3 3are constructors for 2×2 and 3×3 matrices. I've just shown how to make the types.V o i d ) This matrix has the property that its square is zero. It would probably have been better to have used lists like Brent did. I couldn't write a working S h o winstance for F i x . we can use what we've learnt about regular expressions here to solve some numerical problems.m 0 1 )r o w 1 )( I 0 . We can now go back and look at the matrix form of divided differences on wikipedia. I hope you can now see that the matrix Tid(x0. By the way.m 0 1 )r o w 1 )( I 0 .I 0 )=m 1 0 >t y p ei n s t a n c e( : ! )( K 2 2r o w 0( m 1 0 . In other words. It's the 'infinitesimal type' I described here. We've also been doing type-level automatic divided differencing. But we don't yet have an easy way to write functions that respect these constraints. the 1-cells are the types associated with paths from one state to another. But that's just speculation right now.I 0 )=m 0 0 >t y p ei n s t a n c e( : ! )( K 2 2( m 0 0 .com/2010/08/constraining-types-with-regular. this is type-level automatic differentiation.html 9/14 . But I won't write about that until the next article. Can you write one? And an implementation of a r b i t r a r yfor QuickCheck? And I hope you can now solve my problem from last week..m 1 1 ) )( I 1 ..m 1 1 ) )( I 1 . I think what I've described here can be viewed as an application of what Backhouse talks about in these slides.sigfpe.( ) ) > ( V o i d . and the 2-cells are functions between types that respect the constraint.

html 10/14 ..m 0 1 .m 0 1 . I'll leave that as an exercise :-) Posted by Dan Piponi at Saturday.sigfpe.m 1 1 .I 0 )=m 1 0 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0( m 1 0 . *Main> show (Mu1 \$ Left ()) "Left ()" *Main> -} Sunday.I 0 )=m 2 0 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0r o w 1( m 2 0 . UndecidableInstances #-} newtype Mu1 f = Mu1 (f (Mu1 f)) instance Show (f (Mu1 f)) => Show (Mu1 f) where show (Mu1 f) = show f newtype Mu2 f = Mu2 (f (Mu2 f)) -.m 0 2 )r o w 1r o w 2 )( I 0 .I 2 )=m 0 2 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0( m 1 0 .I 1 )=m 0 1 >t y p ei n s t a n c e( : ! )( K 3 3( m 0 0 .m 1 2 )r o w 2 )( I 1 .m 1 1 .m 1 2 )r o w 2 )( I 1 .3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions > >d a t aK 3 3r o w 0r o w 1r o w 2 >t y p ei n s t a n c eD i m( K 3 3r o w 0r o w 1r o w 2 )=I 3 > >t y p ei n s t a n c e( : ! )( K 3 3( m 0 0 .m 0 2 )r o w 1r o w 2 )( I 0 . 15 August. I think you've found a bug in the construction of circular typeclass dictionaries when type families are involved.I 0 )=m 0 0 >t y p ei n s t a n c e( : ! )( K 3 3( m 0 0 .m 2 1 .same as Mu1 type family Id m type instance Id m = m instance Show (Id (f (Mu2 f))) => Show (Mu2 f) where show (Mu2 f) = show f {*Main> show (Mu2 \$ Left ()) C-c C-cInterrupted. FlexibleContexts. 2010 15 comments: Saizan said.I 2 )=m 2 2 Update: I neglected to mention that there is a bit of subtlety with the issue of being able to create the same string by different walks through the automaton.m 1 2 )r o w 2 )( I 1 .m 1 1 .I 1 )=m 2 1 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0r o w 1( m 2 0 ..m 0 2 )r o w 1r o w 2 )( I 0 .m 2 2 ) )( I 2 .m 2 2 ) )( I 2 . You going to report it? http://blog.com/2010/08/constraining-types-with-regular.m 0 1 .I 2 )=m 1 2 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0r o w 1( m 2 0 . Thanks for putting in the work to isolate the problem.m 2 1 ..m 2 2 ) )( I 2 ..I 1 )=m 1 1 >t y p ei n s t a n c e( : ! )( K 3 3r o w 0( m 1 0 . August 14. {-# LANGUAGE TypeFamilies.m 2 1 . 2010 sigfpe said.

com/2010/08/constraining-types-with-regular. 15 August.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions Sunday. 15 August. ()) (Void. with a level of Either a Void less in the values. http://blog. I found it a bit confusing that you used D in the definition of List. The matrices are easily generizable to Brents type lists: > type instance (x ::: xs) :! (i...sigfpe.... Sunday. You say: > iso1' (Fix (Right (Right a))) = error "Can't be called as a is void" But if I replace a with '?' ghc says: Couldn't match expected type `(().. 2010 sigfpe said. 15 August. Fix (S Zero.. 2010 sigfpe said.. 2010 Sjoerd Visscher said. m11) = > ((m00 ::: m01 ::: Nil) ::: > (m10 ::: m11 ::: Nil) ::: Nil) Sunday. 15 August.html 11/14 . Sunday. m01) (m10. Zero) (K22 (x. Well you can't define List' this way without a version of List coming out too :-) Hope it all still makes some kind of sense. 15 August. Something is not right. 2010 Sjoerd Visscher said. I guess it was just an example.. x)) (I :+: (X :*: Y)))' against inferred type `Char' That's not Void! Sunday. 2010 Sjoerd Visscher said.. I0) (K11 x) ListF works as well for the obvious definition of K11. type List x = Fix (I0. j) = ((x ::: xs) :!! i) :!! j > type instance Dim Nil = Zero > type instance Dim (x ::: xs) = S (Dim xs) You can get K22 back as a type family: > type family K22 row0 row1 :: * > type instance K22 (m00.

Nice.. Thanks for the agda version. ()) (Void.com/2010/08/constraining-types-with-regular.). Ow! I actually checked that. 2010 Saizan said. Sunday. protocol sequences. I. just the way you did..haskell.org/trac/ghc/ticket/3500 . Saizan..e.haskell..org/~Saizan/UCodes. 16 August. since (...html 12/14 . x)) (I :+: (X :*: Y)))" won't really reduce to Void. 2010 Saizan said. That type *is* Void. Haven't got time to look into right now. The code *is* passing my tests. 2010 Saizan said. etc) but also code (monadic operations. Monday. Unfortunately fwarn-inplete-pattern and -XEmptyDecls don't play well together and so you test if the code is correct by getting it to generate an error (and conversely as you've found). i've left a comment there. The error message is due to the other annoyance I'm having with ghc: I don't know how to make it fully reduce types in its error messages.fcgi/view?id=29142#a29142 Monday.. Monday. once fully reduced.agda http://blog.sigfpe. but assuming totality and that Fix is a least fixed point we can show that it's isomorphic to it. Monday.3/29/2014 A Neighborhood of Infinity: Constraining Types with Regular Expressions Sjoerd. Zero) (K22 (x. As for the bug i think it's the same problem as http://hackage.) is a type constructor. here another excercise: apply this scheme to model "session types". It seems this scheme could not only be but used to restrict data in a regular fashion (trees. 2010 sigfpe said. Oh.. 16 August.. It'll help me to learn the language.. 2010 heckenpenner_rot said.. 16 August. 2010 sigfpe said.. and kept checking. 15 August. etc.org/fastcgi/hpaste. "((). 16 August... Sunday.Fix (S Zero. I misunderstood. here's the example on how one usually avoids the --no-positivity-check flag: http://code. So I hope it's just a typo that happened during the renaming and reformatting I did at the end. like in this Agda version: http://hpaste. 15 August.