{"id":826,"date":"2009-11-17T09:27:00","date_gmt":"2009-11-17T09:27:00","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2009\/11\/17\/types-in-haskell-types-are-propositions-programs-are-proofs\/"},"modified":"2009-11-17T09:27:00","modified_gmt":"2009-11-17T09:27:00","slug":"types-in-haskell-types-are-propositions-programs-are-proofs","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2009\/11\/17\/types-in-haskell-types-are-propositions-programs-are-proofs\/","title":{"rendered":"Types in Haskell: Types are Propositions, Programs are Proofs"},"content":{"rendered":"<p><em> (This is a revised repost of an earlier part of  my Haskell tutorial.)<br \/>\n<\/em><\/p>\n<p> Haskell is a strongly typed language. In fact, the type system in Haskell<br \/>\nis both stricter and more expressive than any type system I&#8217;ve seen for any<br \/>\nnon-functional language. The moment we get beyond writing trivial<br \/>\ninteger-based functions, the type system inevitably becomes visible, so we<br \/>\nneed to take the time now to talk about it a little bit, in order to<br \/>\nunderstand how it works.<\/p>\n<p><!--more--><\/p>\n<p> One of the most important things to recognize about Haskell&#8217;s type system<br \/>\nis that it&#8217;s based on <em>type inference<\/em>. What that means is that in<br \/>\ngeneral, you <em>don&#8217;t<\/em> need to provide type declarations. Based on how<br \/>\nyou use a value, the compiler can usually figure out what type it is. The net<br \/>\neffect is that in many Haskell programs, you don&#8217;t write <em>any<\/em> type<br \/>\ndeclarations, but your program is still carefully type-checked. You<br \/>\n<em>can<\/em> declare a type for any expression you want, but the only time you<br \/>\n<em>must<\/em> is when something about your code is ambiguous enough that type<br \/>\ninference can&#8217;t properly select one type for an expression.<\/p>\n<p> I&#8217;m not going to go into a lot of detail, but the basic idea behind<br \/>\nHaskell&#8217;s type system is that there&#8217;s a basic propositional logic of types.<br \/>\nEvery type is represented as a statement in the type-logic. So as you&#8217;ll see<br \/>\nbelow, an integer is written as type <code>Integer<\/code>. A function<br \/>\nfrom integers to integers has a type which is an implication statement:<br \/>\n&#8220;<code>Integer -&gt; Integer<\/code>&#8220;, which you can read in the logic as<br \/>\n&#8220;If the input type is Integer then the output type is Integer&#8221;. The<br \/>\nway that type inference works is that values with known types become<br \/>\npropositions; the basic type inference process s just logical inference<br \/>\nover the type logic.<\/p>\n<p> The type logic is designed so that the inference process, when it analyzes<br \/>\nan expression, it will generate the <em>most general<\/em> type; that is, when<br \/>\nit&#8217;s inferring a type for an expression, it will always pick the most general,<br \/>\ninclusive type that can match the expression. And that leads to one<br \/>\ncomplication for beginners: Haskell&#8217;s type system is almost two different type<br \/>\nsystems &#8211; a basic type system, and a <em>meta<\/em>-type system. The meta-type<br \/>\nsystem is based on something called <em>type classes<\/em>, which group related<br \/>\nfamilies of types together. The most general types that come up as a result of<br \/>\ntype inference are frequently based on type classes, rather than on specific<br \/>\nconcrete types.<\/p>\n<p> A type-class in code is basically a <em>predicate<\/em> over a type<br \/>\nvariable. For example, the type of the parameters to the &#8220;<code>+<\/code>&#8221;<br \/>\noperator must be a numeric type. But since &#8220;<code>+<\/code>&#8221; can be used on<br \/>\nintegers, floats, complex, rationals, and so on, the type of the<br \/>\n&#8220;<code>+<\/code>&#8221; operator&#8217;s parameters needs to be something that includes all<br \/>\nof those. The way that Haskell says that is the type is &#8220;<code>Num a =&gt;<br \/>\na<\/code>&#8221; &#8211; that is, that the type a is a member of the type-class<br \/>\n<code>Num<\/code>. The way to understand that is &#8220;Some type &#8216;a&#8217; such that &#8216;a&#8217; is a<br \/>\nnumeric type.&#8221;.<\/p>\n<p> The thing to remember is that essentially, a type-class is a type for<br \/>\ntypes. A <em>type<\/em> can be thought of as a predicate which is only true for<br \/>\nmembers of that type; a <em>type-class<\/em> is essentially a second-order<br \/>\npredicate over <em>types<\/em>, which is only true for types that are members<br \/>\nof the type-class. What type-classes do is allow you to define a <em>general<br \/>\nconcept<\/em> for grouping together a family of conceptually related types.<br \/>\nThen you can write functions whose parameter or return types are formed using<br \/>\na type-class; the type class defines a <em>predicate<\/em> which describes the<br \/>\nproperties of types that could be used in the function. <\/p>\n<p> So if we write a function whose parameters need to be numbers, but don&#8217;t<br \/>\nneed to be a <em>specific kind<\/em> of number, we would write it to use<br \/>\na type-class that described the basic properties of numbers. Then you&#8217;d be<br \/>\nable to use <em>any<\/em> type that satisfied the type-predicate defined<br \/>\nby the type-class &#8220;Num&#8221;.<\/p>\n<p> If you write a function that uses numeric operations, but you don&#8217;t write<br \/>\na type declaration, then Haskell&#8217;s type-inference system will infer types for<br \/>\nit based on the &#8220;<code>Num<\/code>&#8221; type-class: &#8220;<code>Num<\/code>&#8221; is the most<br \/>\ngeneral type-class of numbers; the more things we actually <em>do<\/em> with<br \/>\nnumbers, the more constrained the type becomes. For example, if we use the &#8220;\/&#8221;<br \/>\noperator, instead of inferring that the type of parameter must be an instance<br \/>\nof the &#8220;Fractional&#8221; type-class.<\/p>\n<p> With that little diversion out of the way, we can get back to talking<br \/>\nabout how we&#8217;ll use types in Haskell. Types start at the bottom with a bundle<br \/>\nof <em>primitive<\/em> atomic types which are built in to the language:<br \/>\n<code>Integer<\/code>, <code>Char<\/code>, <code>String<\/code>,<br \/>\n<code>Boolean<\/code>, and quite a few more. Using those types, we can<br \/>\n<em>construct<\/em> more interesting types. For now, the most important<br \/>\nconstructed type is a <em>function type<\/em>. In Haskell, functions are just<br \/>\nvalues like anything else, and so they need types. The basic form of a simple<br \/>\nsingle-parameter function is &#8220;<code>a -&gt; b<\/code>&#8220;, where &#8220;<code>a<\/code>&#8221; is<br \/>\nthe type of the parameter, and &#8220;<code>b<\/code>&#8221; is the type of the value<br \/>\nreturned by the function.<\/p>\n<p> So now, let&#8217;s go back and look at our factorial function. What&#8217;s the type<br \/>\nof our basic &#8220;<code>fact<\/code>&#8221; function? According to Hugs, it&#8217;s<br \/>\n&#8220;<code>Num a =&gt; a -&gt; a<\/code>&#8220;.<\/p>\n<p> Definitely not what you might expect. What&#8217;s happening is that the system<br \/>\nis looking at the expression, and picking the most general type. In fact, the<br \/>\nonly things that are done with the parameter are comparison, subtraction, and<br \/>\nmultiplication: so the system infers that the the parameter must be a<br \/>\n<em>number<\/em>, but it can&#8217;t infer anything more than that. So it says that<br \/>\nthe type of the function parameter is a numeric type; that is, a member of the<br \/>\ntype-class &#8220;<code>Num<\/code>&#8220;; and that the return type of the function is the same as<br \/>\nthe type of the parameter. So the statement &#8220;<code>Num a =&gt; a -&gt; a<\/code>&#8221; basically says<br \/>\nthat &#8220;<code>fact<\/code>&#8221; is a function that takes a parameter of <em>some<\/em> type<br \/>\nrepresented by a <em>type variable<\/em> &#8220;<code>a<\/code>&#8221; and returns a value of the<br \/>\n<em>same<\/em> type; and it also says that the type variable &#8220;<code>a<\/code>&#8221; must be a<br \/>\nmember of the meta-type &#8220;<code>Num<\/code>&#8220;, which is a type-class which includes all of<br \/>\nthe numeric types. So according to Haskell, as written the factorial function<br \/>\nis a function which takes a parameter of <em>any<\/em> numeric type, and returns a<br \/>\nvalue of the <em>same<\/em> numeric type as its parameter.<\/p>\n<p> If we look at that type, and think about what the factorial function actually<br \/>\ndoes, there&#8217;s a problem. That type isn&#8217;t correct, because factorial is only<br \/>\ndefined for integers, and if we pass it a non-integer value as a parameter, it<br \/>\nwill <em>never terminate<\/em>! But Haskell can&#8217;t figure that out for itself &#8211;<br \/>\nall it knows is that we do three things with the parameter to our function: we<br \/>\ncompare it to zero, we subtract from it, and we multiply by it. So Haskell&#8217;s<br \/>\nmost general type for that is a general numeric type. So since we&#8217;d like to<br \/>\nprevent anyone from mis-calling factorial by passing it a fraction (which will<br \/>\nresult in it never terminating), we should put in a type declaration to force<br \/>\nit to take the more specific type &#8220;<code>Integer -&gt; Integer<\/code>&#8221; &#8211; that is,<br \/>\na function from an integer to an integer. The way that we&#8217;d do that is to add<br \/>\nan <em>explicit type declaration<\/em> before the function:<\/p>\n<pre>\n&gt; fact :: Integer -&gt; Integer\n&gt; fact 0 = 1\n&gt; fact n = n*fact(n-1)\n<\/pre>\n<p> That does it; the compiler accepts the stronger constraint provided by our<br \/>\ntype declaration.<\/p>\n<p> So what we&#8217;ve seen so far is that a function type for a single parameter<br \/>\nfunction is created from two other types, joined by the &#8220;<code>-&gt;<\/code>&#8221;<br \/>\ntype-constructor. With type-classes mixed in, that can be <em>prefixed<\/em> by<br \/>\ntype-class constraints, which specify the <em>type-classes<\/em> of any type<br \/>\nvariables in the function type.<\/p>\n<p> Before moving on to multiple parameter functions, it&#8217;s useful to introduce a<br \/>\nlittle bit of syntax. Suppose we have a function like the following:<\/p>\n<pre>\npoly x y = x*x + 2*x*y + y*y\n<\/pre>\n<p> That definition is actually a shorthand. Haskell is a lambda calculus based<br \/>\nlanguage, so semantically, functions are really just lambda expressions: that<br \/>\ndefinition is really just a binding from a name to a lambda expression.<\/p>\n<p> In lambda calculus, we&#8217;d write a definition like that as:<\/p>\n<pre>\npoly &equiv; &lambda; x y . x*x + 2*x*y + y*y\n<\/pre>\n<p> Haskell&#8217;s syntax is very close to that. The definition in Haskell syntax<br \/>\nusing a lambda expression would be:<\/p>\n<pre>\npoly = ( x y -&gt; x*x + 2*x*y + y*y)\n<\/pre>\n<p> The &lambda; in the lambda expression is replaced by a backslash, which is the<br \/>\ncharacter on most keyboards that most resembles lambda; the &#8220;.&#8221; becomes an<br \/>\narrow.<\/p>\n<p> Now, with that out of the way, let&#8217;s get back to multi-parameter functions.<br \/>\nSuppose we take the poly function, and see what Hugs says about the type:<\/p>\n<pre>\npoly x y = x*x + 2*x*y + y*y\nhugs&gt;  Main&gt; :type poly<br \/>\nhugs&gt;  poly :: Num a =&gt; a -&gt; a -&gt; a\n<\/pre>\n<p> This answer is very surprising to most people: it&#8217;s a <em>two<\/em><br \/>\nparameter function. So intuitively, there should by <em>some<\/em> grouping<br \/>\noperator for the two parameters, to make the type say &#8220;a function that takes<br \/>\ntwo a&#8217;s, and returns an a&#8221;; something like &#8220;<code>(a,a) -&gt; a<\/code>&#8220;.\n<\/p>\n<p> But functions in Haskell are automatically <em>curried<\/em>. Currying is a<br \/>\nterm from mathematical logic; it&#8217;s based on the idea that if a function is a<br \/>\nvalue, then you don&#8217;t <em>ever<\/em> need to be able to take more than one parameter.<br \/>\nInstead of a two parameter function, you can write a one-parameter function<br \/>\nthat returns another one-parameter function. Since that sounds really<br \/>\nconfusing, let&#8217;s take a moment and look again at our &#8220;poly&#8221; example:<\/p>\n<pre>\npoly x y = x*x + 2*x*y + y*y\n<\/pre>\n<p> Now, suppose we knew that &#8220;x&#8221; was going to be three. Then we could write a<br \/>\nspecial one-parameter function:<\/p>\n<pre>\npoly3 y = 3*3 + 2*3*y + y*y\n<\/pre>\n<p> But we <em>don&#8217;t<\/em> know &#8220;<code>x<\/code>&#8220;. But what we <em>can<\/em> do<br \/>\nis write a function that takes a <em>parameter<\/em> &#8220;<code>x<\/code>&#8220;, and<br \/>\nreturns a function where all of the references to <code>x<\/code> are filled<br \/>\nin, and given a y value, will return the value of the polynomial for x and<br \/>\ny:<\/p>\n<pre>\npolyn x = (y -&gt; x*x + 2*x*y + y*y)\n<\/pre>\n<p> If we call &#8220;<code>polyn 3<\/code>&#8220;, the result is exactly what we wrote for<br \/>\n&#8220;<code>poly3<\/code>&#8220;. If we call &#8220;<code>polyn a b<\/code>&#8220;, it&#8217;s semantically<br \/>\n<em>exactly<\/em> the same thing as &#8220;<code>poly a b<\/code>&#8220;. (That doesn&#8217;t mean<br \/>\nthat the compiler actually <em>implements<\/em> multi-parameter functions by<br \/>\ngenerating single-parameter functions; it generates multi-parameters functions<br \/>\nthe way you&#8217;d expect. But everything <em>behaves<\/em> as if it did.) So what&#8217;s<br \/>\nthe type of <code>polyn<\/code>? Well, it&#8217;s a function that takes a parameter<br \/>\nof type <code>a<\/code>; and returns a <em>function<\/em> of type &#8220;<code>Num a<br \/>\n=&gt; a -&gt; a<\/code>&#8220;. So, the type of <code>polyn<\/code> is &#8220;<code>Num a =&gt; a -&gt; (a -&gt;<br \/>\na)<\/code>&#8220;; and since the precedence and associativity rules are set up to<br \/>\nmake currying convenient, the parents in that aren&#8217;t necessary &#8211; that&#8217;s the<br \/>\nsame as &#8220;<code>Num a =&gt; a -&gt; a -&gt; a<\/code>&#8220;. Since &#8220;<code>poly<\/code>&#8221; and<br \/>\n&#8220;<code>polyn<\/code>&#8221; are supposed to be semantically equivalent, that means<br \/>\nthat &#8220;<code>poly<\/code>&#8220;&#8216;s type must also be &#8220;<code>Num a =&gt; a -&gt; a -&gt;<br \/>\na'\".<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>(This is a revised repost of an earlier part of my Haskell tutorial.) Haskell is a strongly typed language. In fact, the type system in Haskell is both stricter and more expressive than any type system I&#8217;ve seen for any non-functional language. The moment we get beyond writing trivial integer-based functions, the type system inevitably [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[89],"tags":[],"class_list":["post-826","post","type-post","status-publish","format-standard","hentry","category-haskell"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-dk","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/826","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=826"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/826\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=826"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=826"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=826"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}