The Haskell Sequence - Discussion
http://sequence.complete.org/taxonomy/term/16/0
enRefuting Myths about Polymorphism
http://sequence.complete.org/node/381
<p> I am addressing an article written by Mark Dominus named <a href="http://blog.plover.com/oops/subtypes.html">Subtypes and
polymorphism</a>:
<h1>References and Type Safety</h1>
<p> In the second part of the article, Mark points out an example
which he says demonstrates where Standard ML breaks type safety. In
fact, he is passing along a common myth about the "value restriction"
in Standard ML and other Hindley-Milner based languages. To state it
quite simply up-front: the "value restriction" is not necessary to
protect type safety. It is simply there to preserve an intuition
about how code evaluates.
<p> This is the example he presents. If you give this to a Standard
ML compiler, it will fail on the line beginning with <code>val
a</code> because of the "value restriction."
<p>
<code>
fun id x = x (* id : α → α *)
val a = ref id; (* a : ref (α → α) *)
fun not true = false
| not false = true ; (* not: bool → bool *)
a := not;
(!a) 13
</code>
<p> If the value restriction were to be lifted, the code would still
be type-safe. Mark's interpretation of the code is incorrect. But
the thinking process which led him to his incorrect interpretation is
itself the reason for the value restriction.
<h3>The Value Restriction</h3>
<p>
The restriction itself is simple to describe: if you have code that looks like this:
<p>
<code>
val x = ...
</code>
<p>
and the <code>...</code> has a type involving polymorphism, then it must be a value syntactically.
<p><code>ref id</code> of type <code>(α → α) ref</code> is not a value -- it will
evaluate to a location-value. Also, it is polymorphic. Therefore the
value restriction applies, and the code is not permitted.
<p> The value restriction is seemingly helping us preserve type
safety. But that is a misconception. If you removed the value
restriction, you could compile the above code, and it would still be
type-safe.
<p> The short answer to what would happen is this: the <code>a</code>
on the line <code>a:=not</code> and other one on the line <code>(!a)
13</code> are not evaluating to same location-value! Therefore, you
are not, in fact, applying <code>not</code> to an integer. You are
applying <code>id</code> to an integer, which is perfectly fine.
<h3>Under the hood</h3>
<p> To explain why this is the case, I need to show you that the code
above is omitting important details about polymorphism. Those details
are automatically reconstructed and inserted by the typechecker. Here
is a version of the code that is decorated with the details after
typechecking:
<p>
<code>
val id : ∀α. α → α = Λα. fn (x:α) => x
val a : ∀α. (α → α) ref = Λα. (ref{α → α}) (id{α})
val not : bool → bool
a{bool} := not;
((!{int → int}) (a{int})) 13
</code>
<p> Whew! Now you can see why people prefer to let the typechecker
deal with this stuff. But now the Hindley-Milner style types have
been translated into a System F (aka polymorphic lambda-calculus)
style language.
<p> When you see the type α → α in H-M types, it's omitting the
universal quantifier. Haskell folks are already familiar with it,
because a popular extension allows the explicit use of ∀ (aka
"forall"). All the type variables must be quantified in a ∀ placed at
the beginning of the type.
<p> The term which has a type beginning with ∀ is called Λ (aka
"type-lambda"). Therefore, this term must be introduced everywhere a ∀
appears in the type. The typechecker rewrites your code to insert the
necessary type-lambdas.
<p> In order to use a polymorphic value (a term wrapped in a
type-lambda), you must apply a type to the type-lambda. This is
denoted <code>t{T}</code> in the above example. For example, to use
the polymorphic function <code>id : ∀a. α → α</code>, you must first
apply its type-lambda to the type that you want: <code>(id{int}) 13</code> steps to 13.
<p> Now, I can explain what is happening behind the scenes when you
use <code>a</code>. Since <code>a</code> is polymorphic, that means
it is wrapped in a type-lambda. In order to be used as a reference,
it must be applied to a type. For example:
<p>
<code>a{bool}</code> steps to <code>(ref{bool → bool}) (id{bool})</code> which then steps to <code>l1</code>
<p>
where <code>l1</code> of type <code>(bool → bool) ref</code>
is a location-value –- the result of evaluating a call to
<code>ref</code>.
<p> On the other hand, later on we do:
<p>
<code>a{int}</code> steps to <code>(ref{int → int}) (id{int})</code> which then steps to <code>l2</code>
<p>
where <code>l2</code> is a different location-value. We've called
<code>ref</code> a second time, and it's given us a different
location.
<h3>The Unexpected</h3>
<p> When Mark wrote <code>val a = ref id</code> he clearly did not
expect <code>ref</code> to be called more than once. But I have
demonstrated that is exactly what would happen if his code were to be
compiled and run.
<p> This kind of behavior is type-safe; we never attempt to invoke
<code>not</code> on the integer 13 because the function
<code>not</code> is safely stored in an entirely different location.
It is possible to go ahead and prove the type safety of a language
like this with references. But clearly, the repeated computation of
<code>ref</code> is surprising, and might even be said to
be signs of a "leaky abstraction" in the Hindley-Milner type system.
It attempts to hide the details about type-lambdas, but they've
revealed themselves in the dynamic behavior of the code.
<p> The "value restriction" then, is simply a measure put in place to
prevent "surprising" behavior from occurring in the first place. By
allowing only values syntactically, you will not end up with any
evaluation after applying the type-lambda, and all is as expected.http://sequence.complete.org/node/381#commentsType SystemDiscussionSun, 02 Mar 2008 21:23:35 -0800mrd381 at http://sequence.complete.orgSubtyping and Polymorphism
http://sequence.complete.org/node/380
<p> I am addressing an article written by Mark Dominus named <a href="http://blog.plover.com/oops/subtypes.html">Subtypes and
polymorphism</a>:
<p>
There are two parts to this article, the first discusses the fact that
subtyping and polymorphism can lead to some unintuitive results. I
won't repeat his point here, but just note that the term for the
behavior of "List" here is called "invariant" and the incorrect
behavior which he had assumed is called "covariant."
<h1>Java's broken arrays</h1>
<p>
While Java generic Lists are "invariant," Java arrays are
"covariant." This is not due to some wonderful property of arrays,
but in fact is a gigantic flaw in the Java language which breaks
static type safety. As a result, arrays must carry run-time type
information in order to dynamically check the type of objects being
inserted. Consider this code:
<p>
<code>
String stringA[] = new String[1];
stringA[0] = "Hello";
Object objectA[] = stringA;
</code>
<p>
So far this seems okay -- String is a subtype of Object -- therefore
it seems safe to say that String arrays are a subtype of Object
arrays. This is called "covariant" behavior.
<p>
So if objectA is an array of Objects, and everything is a subtype of
Object, then it should be okay to put an Integer there instead, right?
<p>
<code>
objectA[0] = 1;
</code>
<p>
but wait, now <code>stringA[0]</code> has an Integer in it! A String
array cannot have an Integer in it. Before it seemed that arrays
might be "covariant," but mutation makes it seem that arrays must be
"contravariant." We can't be both at the same time, so it seems
mutable arrays are "invariant" after all.
<p>
If you try running the code given above, you get:
<p>
<code>
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
</code>
<p>
clearly, a run-time type-check is being performed.
<h3>About Covariance, Contravariance, and Invariance</h3>
<p> I've been using these terms, but haven't really defined them.
<p> A type-constructor <code>C</code> is covariant iff:
for all types <code>S,T</code>, if <code>S</code> is a subtype of
<code>T</code> then <code>C<S></code> is a subtype of
<code>C<T></code>.
<p> Similarly, for contravariance, if <code>S</code> is a subtype of
<code>T</code> then <code>C<T></code> is a subtype of
<code>C<S></code>. Note that <code>S</code> and <code>T</code>
have swapped.
<p> Invariance (the name is confusing, I know) is the lack of
covariance or contravariance.
<p> It may be of interest to know that functions are contravariant in
the input, but covariant in the output. Perhaps that is why the Java
folks are not so eager to have function types!
<h2>Broken arrays and generics</h2>
<p>
The brokenness of Java arrays has impacted on the design of Java
generics as well. Consider the following code that might be written
if you were designing your own generic container of some sort:
<p>
<code>
public class Container<T> {
private T[] a;
</code>
<p>
So far so good, right? An array of some parameterized type seems
reasonable.
<p>
<code>
public Container() {
a = (T[])new T[10];
}
}
</code>
<p>
Uh oh!
<p>
<code>
Container.java:5: generic array creation
a = (T[])new T[10];
^
Note: Container.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
</code>
<p>
No can do. Java is jumping down my throat over this seemingly
reasonable code. Why? Generic arrays are not permitted like this,
because -- as described above -- Java arrays must carry run-time type
information. But Generics promise total type erasure -- meaning NO
run-time type information is kept. Sounds like they designed
themselves into a corner when creating Java, and later, Generics.
<h1>Polymorphism is not Subtyping</h1>
<p>
Returning to the second part of the article, I want to dispel a few
common misperceptions about polymorphism.
<p>
Mark Dominus:
<cite> Then we define a logical negation function, not, which
has type bool → bool, that is, it takes a boolean argument and
returns a boolean result. Since this is a subtype of α → α, we can
store this function in the cell referenced by a.
</cite>
<p> I want to make it absolutely clear that bool → bool is not a
subtype of α → α. There is no notion of subtyping in basic
Hindley-Milner, and it is still not the case even in a "loose" sense.
<p> The easiest way to verify this for yourself is to find your
nearest Standard ML (or similar) prompt and type this:
<p>
<code>
not : 'a -> 'a
</code>
<p> and get a type error. <code>not : bool → bool</code> and it is
not of type α → α.
<p> Now, his example does not typecheck in Standard ML because of the
"value restriction." The value restriction does not exist to preserve
type safety at all, but only to prevent programmer confusion.
<p> This post has gone on too long about Java, so I am afraid it will
wash out the more important points about polymorphism and the value
restriction. I will leave it then, to the next post.http://sequence.complete.org/node/380#commentsType SystemDiscussionSun, 02 Mar 2008 19:54:23 -0800mrd380 at http://sequence.complete.orgDo arrows, monads, monad transformers clash when a large app needs libraries which use each of these?
http://sequence.complete.org/node/379
<p><STRONG>Now for my question</STRONG></p>
<p>from my neophyte perspective, Clean has a single easy-to-use mechanism for state maintenance - uniqueness typing.</p>
<p>Haskell has monads.</p>
<p>But then it also has arrows now.</p>
<p>And so there are two XML products, one based on arrows and one based monads. </p>
<p>When one is developing a large project in Haskell, what does one do if certain libraries are done in arrows and others in monads and you need to use both? And what about monad-transformers? Is that not yet a 3rd complication?</p>
<p>A Java programmer only has objects to handle any concept. So the idea of one library doing one thing and another doing another does not come up. </p>
<p>So, is there a difficulty in large application development because of the clashing advanced functional metaphors of arrow, monad, monad transformer?</p>
<p>Let's face it. There are very few large-scale products shipped in Haskell. Period. I dont know how good that is. or why that is.</p>
<p>I suppose Galois is happy with haskell, but the lack of large useable apps make you wonder if something like what I mention is getting in the way.</p>
<p><STRONG>About my level and interest haskell</STRONG></p>
<p>I have been through SJT's book. Been through the Haskell Road. Been through Sethi's "Algorithms". Like Bird and Wadler's intro to FP.</p>
<p>Now, I first got interested in Haskell in 2003. I love haskell. It is profoundly elegant. I never quite got over the hump into making it useful... 8 years as a Perl developer spoils you. But as a Perl developer, I think very functionally and studying Haskell has helped me immensely. But i always run back to my crutches when the game gets tight.</p>
http://sequence.complete.org/node/379#commentsMiscellaneousDiscussionMon, 25 Feb 2008 10:59:21 -0800metaperl379 at http://sequence.complete.orgAn Exersize in Over-Engineering
http://sequence.complete.org/node/376
<h1>Intro</h1>
<p>Inspired by an old <a href="http://www.ddj.com/architect/184414835">article on over engineering</a> I decided to try and over design / bloat an event system (aka a timeout system). This is the same system I posted about earlier, but with fewer features and many more LOC.</p>
<h1>Conception</h1>
<p>The initial concept was clean, simple. It would handle hundreds of events, be useful for 80% of the cases (I do like the 80% rule). My event system state was this:</p>
<code>
data EventState = EvtState { esEvents :: [Event],
esAlarm :: ClockTime,
esNextId :: EventId }
</code>
<p>Each new event gets a new Id number, the 'NextId' is incremented, and the event is added to the time ordered list, 'esEvents'. 'esAlarm' is updated as necessary (atomically by the thread adding the event with an earlier expire time).</p>
<p>Functionally, there are 2 threads in the system and 'n' threads of event producers. One thread, eventExpire, would threadDelay until the soonest event needs to be expired. The other thread, watchAlarm, would keep an eye on esAlarm - if it was changed to an earlier time then an exception would be thrown to the eventExpire thread.</p>
<h1> Theoretical Bugs!</h1>
<p>Oh no! You could theoretically overflow the Int used to express EventId. I could use Integer, but that still has a theoretical limit and the memory used for ids would grow (eventually). So, I set off to redefine EventId from an Int to a (ClockTime,Int) combo. How do I know this doesn't overflow? Because my new state field which keeps track of the next event Id for each ClockTime (less the expired times), and if it does overflow for a given clock time I refuse to add the event.</p>
<h1>Realistic Performance Issues</h1>
<p>That's right! A close runner up for theoretical bugs is fixing performance. Why is this slow for large numbers of events? Well, adding events is O(n), expiring events can are in order in the list, and updating the alarm time is O(1).</p>
<p>Lets turn _everything_ into a mapping. This way most operations of interest are ~O(ln(n)) The new state will look like this:</p>
<code>
type EventSet = (EventNumber, Map EventNumber (IO ()))
data EventSystem = EvtSys {
esEvents :: TVar (Map ClockTime EventSet), -- Pending Events
esThread :: TVar (Maybe ThreadId), -- Thread for TimerReset exceptions
esAlarm :: TVar ClockTime, -- Time of soonest event
esNewAlarm :: TVar Bool, -- new/earlier alarm
}
</code>
<p>What is this in English? esEvents is a mapping of ClockTimes to a mapping of EventNumbers to IO Actions (and the next unique EventNumber is in there). Also, there is now esNewAlarm, so the alarmWatch can avoid throwing expires unnecessarily.</p>
<p>All in all this does OK, 3 seconds to add and execute 100,000 actions (vs a previous 38 seconds for 10,000).</p>
<h1>Fixing Dropped Events</h1>
<p>Now we have eliminated theoretical bugs and improved performance - whats left? Fixing real bugs of coarse! When running the test code (shamelessly stolen from control-timeout - thanks Adam!) I found some tests never finished and no more events were pending. The cause seems to be that between the event list being pruned of old events and those events being executed the thread was getting an exception (a new event was added with an earlier alarm). The solution is yet another thread and TVar (that's right, I've an irrational dislike of 'block'). Old events are atomically pruned and written to an 'expired' list - then the third thread, monitorExpiredQueue, can zero the queue and execute the actions. My new state has the one more TVar:</p>
<code>
esExpired :: TVar [[EventSet]]
</code>
<h1>A Useful API</h1>
<p>So now that I've finished all this, and we are two or three days after my original post, I need to make the API something useful. Current code looks like:</p>
<code>
sys <- initEventSystem
(TOD sec ps) <- getClockTime
eid <- addEvent sys (TOD (sec+delay) ps) evt
...
if dontNukeThem then cancelEvent sys eid else return ()
-- explosives concept borrowed from recent -cafe example
</code>
<p>Adam had a better idea for most use cases in that they work based on deltas. So I wrote a shim to provide the Control.Timeout API with the Control.Event engine (Control.Event.Timeout).</p>
<code>
eid <- addTimeout act delay
if dontNukeThem then cancelTimeout eid else return ()
</code>
<pre>
And an informal performance comparison is:
Control.Event can add 100K events in 3 seconds
Control.Timeout 100K 12 seconds
Control.Event.Timeout 100K 58 seconds
</pre>
<p>Control.Event.Timeout calls getClockTime on every event add, so driving the Control.Event code with time deltas is really slow. Control.Timeout is obviously very good with time deltas.</p>http://sequence.complete.org/node/376#commentsLibrariesDiscussionWed, 20 Feb 2008 19:44:48 -0800TomMD376 at http://sequence.complete.orgData Parallel Bellman-Ford
http://sequence.complete.org/node/371
<p>Graph representation is as an edge-list.</p>
<p><code><br />
bmf :: Int -> UArr (Int :*: Int :*: Double) -> Int -> UArr Double<br />
bmf n es src = iterate rnd dm0 !! (n - 1)<br />
where<br />
dm0 = toU [ if i == src then 0 else inf | i <- [0 .. n - 1] ]<br />
rnd dm =<br />
updateU dm<br />
(mapU (\ e -><br />
if distOf dm (destin e) > distOf dm (source e) + weight e<br />
then (destin e) :*: (distOf dm (source e) + weight e)<br />
else (destin e) :*: (distOf dm (destin e)))<br />
es)<br />
</code><br />
<code><br />
source = fstS . fstS<br />
destin = sndS . fstS<br />
weight = sndS<br />
distOf dm u = dm !: u<br />
</code><br />
<code><br />
inf :: Double<br />
inf = 1 / 0<br />
</code></p>
<p>The above code uses NDP but only the sequential portions. In order to get parallelism I need to invoke the Distributed operations. However, there are also Unlifted.Parallel operations which hide the usage of the distributed ops.</p>
<p><code><br />
bmf :: Int -> UArr (Int :*: Int :*: Double) -> Int -> UArr Double<br />
bmf n es src = iterate (rnd es) dm0 !! (n - 1)<br />
where<br />
dm0 = toU [ if i == src then 0 else inf | i <- [0 .. n - 1] ]<br />
</code></p>
<p><code><br />
{-# INLINE rnd #-}<br />
rnd :: UArr (Int :*: Int :*: Double) -> UArr Double -> UArr Double<br />
rnd es dm = updateU dm<br />
. mapUP sndS<br />
. filterUP fstS<br />
. mapUP (\ e -><br />
let d = distOf dm (destin e)<br />
d' = distOf dm (source e) + weight e<br />
in if d > d'<br />
then True :*: (destin e :*: d')<br />
else False :*: (0 :*: 0))<br />
$ es<br />
</code></p>
<p>mapUP is the unlifted parallelized version of mapU. However there's no updateUP. Looking into the code I spotted out a commented out version of updateUP. There are problems when figuring out what to do about multiple concurrent writes to one location. NESL specifies "arbitrary" resolution of conflicting concurrent writes. Unfortunately the commented code has bit-rotted and I haven't successfully managed to fix it.</p>
<p>I also added some filtering to prevent it from getting stuck writing Infinity over a real distance constantly, due to "arbitrary" resolution of conflicting writes in updateU. The iterative function is now factored out into its own toplevel definition, for clarity.</p>
http://sequence.complete.org/node/371#commentsNetworkingDiscussionThu, 29 Nov 2007 17:48:33 -0800mrd371 at http://sequence.complete.orgMy Haskell Emacs configuration
http://sequence.complete.org/node/365
<p>I generally like some of the features that come with haskell-mode for Emacs. But I cannot reconcile my style with that of the "smart" indentation mode. I'm not too keen on the default indentation function of Emacs either, at least, not for Haskell code. I use the basic tab-to-tab-stop function but it needs a little tweaking to bring it down to reasonable levels of indentation.</p>
<p>Tabs should never be used.<br />
<code><br />
(setq-default indent-tabs-mode nil)<br />
</code></p>
<p>I like align-regexp, a neat tool from Emacs 22.<br />
<code><br />
(global-set-key (kbd "C-x a r") 'align-regexp)<br />
</code></p>
<p>Basic haskell-mode loading:<br />
<code><br />
(add-to-list 'load-path "path/to/haskell-mode")<br />
(load-library "haskell-site-file")<br />
(add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-mode))<br />
</code></p>
<p>My indentation settings. I wrote my own "newline and indent" function which brings any code you split onto the newline back up to the same indentation level it was at previously.<br />
<code><br />
(remove-hook 'haskell-mode-hook 'turn-on-haskell-indent)<br />
;; Just use tab-stop indentation, 2-space tabs<br />
(add-hook 'haskell-mode-hook<br />
(lambda ()<br />
(turn-on-haskell-doc-mode)<br />
(turn-on-haskell-simple-indent)<br />
(setq indent-line-function 'tab-to-tab-stop)<br />
(setq tab-stop-list<br />
(loop for i from 2 upto 120 by 2 collect i))<br />
(local-set-key (kbd "RET") 'newline-and-indent-relative))<br />
(defun newline-and-indent-relative ()<br />
(interactive)<br />
(newline)<br />
(indent-to-column (save-excursion<br />
(forward-line -1)<br />
(back-to-indentation)<br />
(current-column))))<br />
</code></p>
<p>And just in case you were wondering how to tweak syntax highlighting colors in your .emacs, here's what I do:<br />
<code><br />
(set-face-bold-p 'font-lock-builtin-face nil)<br />
(set-face-bold-p 'font-lock-comment-face nil)<br />
(set-face-bold-p 'font-lock-function-name-face nil)<br />
(set-face-bold-p 'font-lock-keyword-face nil)<br />
(set-face-bold-p 'font-lock-variable-name-face nil)<br />
(set-face-foreground 'font-lock-builtin-face "cyan")<br />
(set-face-foreground 'font-lock-comment-face "pale green")<br />
(set-face-foreground 'font-lock-function-name-face "green")<br />
(set-face-foreground 'font-lock-variable-name-face "pale green")<br />
</code><br />
I think it's fairly self-explanatory. You can play around with the values and find what you like. <code>M-x list-colors-display</code> is a handy tool to list out all the possible colors along with examples.</p>
http://sequence.complete.org/node/365#commentsMiscellaneousDiscussionFri, 02 Nov 2007 11:18:44 -0700mrd365 at http://sequence.complete.orgStrongly Specified Functions
http://sequence.complete.org/node/363
<h3>How to make writing easy functions hard.</h3>
<p>
Kidding aside, a strongly specified function in Coq not only computes but simultaneously provides a proof of some property.
The construction of the proof is integrated with the body of the function. This differentiates it from "weakly" specified functions, which are written separately from the proof.
</p>
<p>
Proving properties about functions is one of the main practical uses of Coq. Unfortunately, it's more difficult than simply writing code. But it is more interesting (and useful) than, say, simple proofs about the monad laws. One thing I learned, the hard way, is that it's better to start small.
</p>
<p>
The first example to talk about is
<a href="http://www.decidable.org/coq/strongspecs/reverse.html">reverse.</a>
It has a simple inductive definition, so that isn't a big deal, like it can be for some things. It uses basic structural induction on lists, so I don't need to define my own well founded induction principle. Hopefully, it illustrates the meaning of "integrated computation and proof."
</p>
<p>
I've split the property up into two inductive definitions. The first one is the actual "reversed" relation between two lists. Notice that it is not necessarily in <code>Set</code>. The second definition is what actually gets constructed by the function. It says: in order to construct a reversed list of <code>xs</code>, you need to supply a list <code>xs'</code> and a proof of <code>reversed xs xs'</code>.
</p>
<p>
The definition is slightly different than other functions you may have seen in Coq (or not). It actually looks more like a theorem proof than a function. This is a good thing.
</p>
<p>
I use the <code>refine</code> tactic now extensively. This tactic allows you to specify an exact proof term, like <code>exact</code>, but also to leave "jokers" to be filled in by further elaboration. The two spots I've left correspond to the base case and the inductive case, which you should be able to figure out from the <code>match</code>. Side note: the <code>return</code> type-annotation is necessary, otherwise type reconstruction won't figure it out.
</p>
<p>
At this point, I've told Coq that there is a <code>nil</code> case and a "cons" case. The <code>nil</code> case is easy, so I just supplied an exact proof term. You can read that as saying: "The reverse of <code>nil</code> is <code>nil</code> and a proof of this is <code>reversed_nil</code>."
</p>
<p>
For the inductive case, I will use <code>refine</code> again to drill deeper into the function and the proof. Notice that the recursive call is made with <code>match</code>. This allows me to separate the recursively computed value <code>xs'</code> from the usage of the induction hypothesis <code>Hxs'</code>. There is a little trap here -- if you use automation prior to making the recursive call you may get a "Proof completed" message. But you won't be able to save the proof, because the "termination checker" will yell at you. The same thing happens if you incorrectly invoke the recursion.
</p>
<p>
The "proof completed" message isn't bogus -- it's just that the function the <code>auto</code> tactic had proven was not the function for which we were looking.
</p>
<p>
The <code>refine</code> statement provides the necessary hint to the theorem prover. I construct the returned data in a separate <code>refine</code> just to illustrate how you can drill down with the tool. This just leaves the simple proof that <code>xs'++(x::nil)</code> is in fact, the reverse.
</p>
<p>
<code>Recursive Extraction</code> pulls out all the computation-related (stuff in <code>Set</code>) bits while leaving behind the proof-related bits. If you want it in Haskell, as always, change the language with <code>Extraction Language Haskell.</code>
</p>
<p>
(All examples were written using Coq 8.1pl1)
</p>http://sequence.complete.org/node/363#commentsType SystemDiscussionSun, 23 Sep 2007 20:37:24 -0700mrd363 at http://sequence.complete.orgRevisiting board-building in chess
http://sequence.complete.org/node/361
<p>So, in my last blog, we looked at the beginnings of a chess engine. That was nearly 2 months ago. I've been kicking around some possibilities for the last few months, trying to come up with a somewhat elegant solution. Ultimately, I've wound up completely refactoring what I started with. I'll be posting more on that here within the next few weeks, but I'm really excited by how one piece came out, so I'd like to show that part at least.</p>
<p>Last time, I mentioned FEN notation for the state of a chess game. Let's look at that in a bit more detail. Here's the FEN notation for the start of a game:</p>
<p>rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1</p>
<p>You can see, there are 6 space-separated fields in this string. What I want to write is this function:</p>
<p>loadFEN :: String -> GameState</p>
<p>...which constructs the state of the game from the FEN. Now, this is notoriously tedious in most languages, but the solution in haskell looks quite elegant to me. </p>
<p>So, let's see. Each field in the FEN is going to tell us something about how to construct the GameState. The first field tells us where the pieces are, the second tells us whose turn it is, the third deals with castling statuses, etc. So we can start with an empty state, and apply multiple functions to it, based on the fields, to build up a new state. That sounds like a fold pattern to me!</p>
<p>However, this seems to be a bit tricky at first. We'll want to apply a different function for each field. That is, the first function will look at that first field and build up the actual squares on the board with pieces. The second function will parse the indicator of whose turn it is, and make that change to the state, etc. So let's start by zipping these functions up with the fields they correspond with:</p>
<pre>
loadFEN fen = foldl apply emptyGameState (zip funcs fields)
where fields = words fen
funcs = [parseBoard, parseCastleStatus]
</pre><p>
Here, I've just listed the functions for the first 2 fields, to give us an idea. The only mystery that's left is this apply function. Let's look at its type:</p>
<p>apply :: GameState -> (String -> GameState -> GameState, String) -> GameState</p>
<p>That is, it takes a current state, and a pair, consisting of a function and a field to apply it to, and a field, and returns a new state. Well, that's not so bad:</p>
<p>apply state (function, field) = function field state</p>
<p>Running this through the @pl formatter on lambdabot in #haskell gives us an ugly mess:</p>
<p>apply = (`ap` snd) . flip (flip . fst)</p>
<p>Hmm, this is interesting though, look at the flipping going on. What if we took the input in a different order? Suppose we wrote this:</p>
<p>apply (function, field) state = function field state</p>
<p>Running this through @pl gives the very elegant:</p>
<p>apply = ap fst snd</p>
<p>Thus, the final code winds up being:</p>
<pre>
loadFEN :: String -> GameState
loadFEN fen = foldl apply emptyGameState (zip funcs fields)
where fields = words fen
apply = flip (ap fst snd)
funcs = [parseBoard, parseCastleStatus]
</pre><p>
Very nifty! Now all the real work gets moved off to smaller functions, which just have to worry about the particular field they're designed to work with. Not only that, but if I only want to deal with the first few fields for now, they're the only ones I have to parse, and I'll still wind up with a valid GameState.</p>
http://sequence.complete.org/node/361#commentsApplicationsDiscussionSat, 18 Aug 2007 11:42:53 -0700chessguy361 at http://sequence.complete.orgCoq and The Monad Laws: The Third
http://sequence.complete.org/node/360
<h2>The Third Monad Law</h2>
<p>
The previous two articles introduced Coq and the first two Monad Laws.
I am discussing the third one separately because it will take longer
to prove.
</p>
<p>
The proof for the third law will proceed at first like the others,
induction and some simplification.
</p>
<p><code>
Theorem third_law :
forall (A B C : Set) (m : list A)
(f : A -> list B) (g : B -> list C),
bind B C (bind A B m f) g =
bind A C m (fun x => bind B C (f x) g).
Proof.
induction m.
(* base case *)
simpl.
trivial.
(* inductive case *)
intros f g.
simpl.
unfold bind.
unfold bind in IHm.
</code></p>
<p>Which brings us to this state in the interactive theorem prover.</p>
<p><code>
1 subgoal
A : Set
B : Set
C : Set
a : A
m : list A
IHm : forall (f : A -> list B) (g : B -> list C),
flat_map g (flat_map f m) =
flat_map (fun x : A => flat_map g (f x)) m
f : A -> list B
g : B -> list C
============================
flat_map g (f a ++ flat_map f m) =
flat_map g (f a) ++ flat_map (fun x : A => flat_map g (f x)) m
</code></p>
<p>
At this point, if we could rewrite
<code>flat_map g (f a ++ flat_map f m)</code>
into
<code>flat_map g (f a) ++ flat_map g (flat_map f m))</code>
then we would be able to apply the Inductive Hypothesis and
be home free.
</p>
<p>
The "cut" tactic allows you to make an assumption, and then later
come back and prove your assumption correct. Using "cut",
</p>
<p><code>
cut (flat_map g (f a ++ flat_map f m) =
flat_map g (f a) ++ flat_map g (flat_map f m)).
intro Distrib.
rewrite Distrib.
rewrite IHm.
reflexivity.
</code></p>
<p>
the original goal is easily solved. But Coq has generated an
additional subgoal: we must now prove that this cut is correct.
</p>
<p><code>
1 subgoal
A : Set
B : Set
C : Set
a : A
m : list A
IHm : forall (f : A -> list B) (g : B -> list C),
flat_map g (flat_map f m) =
flat_map (fun x : A => flat_map g (f x)) m
f : A -> list B
g : B -> list C
============================
flat_map g (f a ++ flat_map f m) =
flat_map g (f a) ++ flat_map g (flat_map f m)
</code></p>
<p>
We'll proceed by induction on <code>f a</code> which
has inductive type <code>list B</code>.
</p>
<p><code>
induction (f a).
(* base case *)
simpl.
reflexivity.
(* inductive case *)
simpl.
rewrite IHl.
rewrite app_ass.
reflexivity.
Qed.
End ListMonad.
</code></p>
<p>
All done. We only needed the association property of list append,
which I found by querying <code>SearchAbout app.</code>
</p>
<h2>Summary</h2>
<p>
Here is a much shorter proof which takes advantage of some of Coq's
automated tactics.
</p>
<p><code>
Theorem third_law' :
forall (A B C : Set) (m : list A)
(f : A -> list B) (g : B -> list C),
bind B C (bind A B m f) g =
bind A C m (fun x => bind B C (f x) g).
Proof.
induction m; simpl; intuition.
replace (bind B C (f a ++ bind A B m f) g)
with (bind B C (f a) g ++ bind B C (bind A B m f) g);
[ rewrite IHm
| induction (f a); simpl; auto;
rewrite app_ass; rewrite IHl
]; auto.
Qed.
</code></p>
<p>
On a final note, Coq has the ability to extract code into
several different languages.
</p>
<p><code>
Extraction Language Haskell.
Recursive Extraction bind ret.
</code></p>
<p>results in</p>
<p><code>
module Main where
import qualified Prelude
data List a = Nil | Cons a (List a)
app l m = case l of
Nil -> m
Cons a l1 -> Cons a (app l1 m)
flat_map f l = case l of
Nil -> Nil
Cons x t -> app (f x) (flat_map f t)
ret :: a1 -> List a1
ret a = Cons a Nil
bind :: (List a1) -> (a1 -> List a2) -> List a2
bind m f = flat_map f m
</code></p>http://sequence.complete.org/node/360#commentsType SystemDiscussionThu, 16 Aug 2007 13:18:03 -0700mrd360 at http://sequence.complete.orgCoq and The Monad Laws: The First and Second
http://sequence.complete.org/node/359
<h2>The First Monad Law</h2>
<p>
In the previous article, I gave a brief introduction to Coq and the List Monad.
I listed three Theorems but did not prove them. Now I will show how to prove the
first two Theorems.
</p>
<p>
Here is the context of our code:
</p>
<p>
<code>
Section ListMonad.
Require Import List.
Definition ret (A : Set) (a : A) := a :: nil.
Definition bind (A B : Set) (m : list A) (f : A -> list B) :=
flat_map f m.
</code>
</p>
<p>The first Theorem, restated:</p>
<p>
<code>
Theorem first_law : forall (A B : Set) (a : A) (f : A -> list B),
bind A B (ret A a) f = f a.
</code>
</p>
<p>At this point, Coq will enter an interactive proof mode. In
<code>coqtop</code>, the prompt will change accordingly. In
ProofGeneral Emacs, a new window will pop up. In either case,
you will be presented with a view of the current assumptions and goals
that looks like this:
</p>
<p>
<code>
1 subgoal
============================
forall (A B : Set) (a : A) (f : A -> list B), bind A B (ret A a) f = f a
</code>
</p>
<p>There are no assumptions, and one goal to prove.</p>
<p>
<code>
Proof.
intros A B a f.
</code>
</p>
<p>Coq proofs can proceed with "tactics" which instruct the
proof engine to perform a certain operation, or series of operations.
In this case, we are introducing assumptions named <code>A,B,a,f</code>
drawn from the antecedents of the goal. Now the proof view looks like this:</p>
<p><code>
1 subgoal
A : Set
B : Set
a : A
f : A -> list B
============================
bind A B (ret A a) f = f a
</code></p>
<p>The "unfold" tactic takes the definition of the given
argument and tries to substitute it directly into the goal.</p>
<p><code>
unfold bind.
unfold ret.
unfold flat_map.
</code></p>
<p>Now we have a vastly simplified goal.</p>
<p><code>
1 subgoal
A : Set
B : Set
a : A
f : A -> list B
============================
f a ++ nil = f a
</code></p>
<p>Everyone knows that appending <code>nil</code> to a list returns the same list.
But how do we explain this to Coq? Search and find out if Coq already
knows about this fact.</p>
<p><code>
SearchRewrite (_ ++ nil).
</code></p>
<p><code>SearchRewrite</code> is used to search for theorems of the form
<code>a = b</code> which can be used to rewrite portions of your goal.
I use the wildcard <code>_</code> to indicate that I want to find any
theorem involving appending <code>nil</code> to anything. Coq finds:
</p>
<p><code>
app_nil_end: forall (A : Set) (l : list A), l = l ++ nil
</code></p>
<p>
We can use this with the "rewrite" tactic. We want to
rewrite our code which looks like <code>l ++ nil</code>
into <code>l</code>. That is a right-to-left rewrite,
which is specified like so:
</p>
<p><code>
rewrite <- app_nil_end.
</code></p>
<p>And finally we have a goal <code>f a = f a</code> which
Coq can trivially solve with</p>
<p><code>
reflexivity.
</code></p>
<p>Or any of the more automated tactics, like "trivial" or "auto".</p>
<p><code>
Qed.
</code></p>
<p>The first monad law is discharged and proven. You can run
<code>Check first_law.</code> to see the new theorem.</p>
<h2>The Second Monad Law</h2>
<p>This second proof will proceed by induction on the structure of the
list parameter <code>m</code>. The "induction" tactic causes Coq to
try a proof by induction. The proof will be split into two parts,
based on case breakdown of the list type.
<p><code>
Theorem second_law : forall (A : Set) (m : list A),
bind A A m (ret A) = m.
Proof.
induction m.
</code></p>
The first is the base case for <code>nil</code>, and
the second is the inductive case for <code>cons</code>.
<p><code>
2 subgoals
A : Set
============================
bind A A nil (ret A) = nil
subgoal 2 is:
bind A A (a :: m) (ret A) = a :: m
</code></p>
<p>The base case is easy, the "simpl" tactic
can take care of the unfolding and simplification.</p>
<p><code>
simpl.
reflexivity.
</code></p>
<p>The inductive case is also straightforward, because
after the "simpl" step, the Inductive Hypothesis is
already applicable. The appropriate <code>IHm</code>
assumption is provided by the "induction" tactic.</p>
<p><code>
simpl.
rewrite IHm.
reflexivity.
Qed.
</code></p>
<h2>Summary</h2>
<p>The full proofs, minus the commentary:</p>
<p><code>
Theorem first_law : forall (A B : Set) (a : A) (f : A -> list B),
bind A B (ret A a) f = f a.
Proof.
intros A B a f.
unfold bind.
unfold ret.
unfold flat_map.
rewrite <- app_nil_end.
reflexivity.
Qed.
Theorem second_law : forall (A : Set) (m : list A),
bind A A m (ret A) = m.
Proof.
induction m.
(* base case *)
simpl.
reflexivity.
(* inductive case *)
simpl.
rewrite IHm.
reflexivity.
Qed.
</code></p>
<p>Next time, the Third Monad Law, and a more complicated proof.</p>http://sequence.complete.org/node/359#commentsType SystemDiscussionWed, 15 Aug 2007 15:14:06 -0700mrd359 at http://sequence.complete.org