I am working on a compiler/proof checker, and I was wondering, if I had a syntax tree such as this, for example:
data Expr
= Lambdas (Set String) Expr
| Var String
| ...
if there were a way to check the alpha-equivalence (equivalence modulo renaming) of Exprs. This Expr, however, is different from the lambda calculus in that the set of variables in a lambda is commutative -- i.e. the order of parameters does not factor in to the checking.
(For simplicity, however, Lambda ["x","y"] ... is distinct from Lambda ["x"] (Lambda ["y"] ...), and in that case the order does matter).
In other words, given two Exprs, how can one efficienly find a renaming from one to the other? This kind of combinatorial problem smells of NP-complete.
The commutativity of the parameters does hint at an exponential comparision, true.
But I suspect you can normalize the parameter lists so you only have to compare them in single order. Then a tree compare with renaming would be essentially linear in the size of the trees.
What I suggest doing is that for each parameter list, visit the subtree in (in-order, postorder, doesn't matter as long as you are consistent) and sort the parameter by the index of the order in which the visit first encounter a parameter use. So if you have
lambda(a,b): .... b ..... a ... b ....
you'd sort the parameter list as:
lambda(b,a)
because you encounter b first, then a second, and the additional encounter of b doesn't matter. Compare the trees with the normalized parameters list.
Life gets messier if you insist the the operators in a lambda clause can be commutative. My guess is that you can still normalize it.
We can appeal to Daan Leijen's HMF for a few ideas. (He dealing with binders for 'foralls', which also come across as commutative.
In particular, he rebinds the variables in the occurrence order in the body.
Then comparison of terms involves skolemizing both the same way and comparing the results.
We can do better than that by replacing that skolemization pass with a locally nameless representation.
data Bound t a = Bound {-# UNPACK #-} !Int t | Unbound a
instance Functor (Bound t) where ...
instance Bifunctor Bound where ...
data Expr a
= Lambdas {-# UNPACK #-} !Int (Expr (Bound () a))
| Var a
So now occurrences of Bound under a lambda are the variables bound directly by the lambda, along with any type information you want to put in the occurence, here I just used ().
Now closed terms are polymorphic in 'a' and, if you sort the elements of the lambda by their use site (and can ensure that you always canonicalize the lambda by removing unused terms) alpha equivalent terms compare simply with (==), and if you need open terms you can work with Expr String or some other representation.
A more anal retentive version of the signature for Expr and Bound would use an existential type and a type level natural to identify the number of variables being bound, and use 'Fin' in the Bound constructor, but since you already have to maintain the invariant that you bind no more variables than the # occurring in the lambda and that the type information agrees across all of Var (Bound n _) with the same n, its not too much of a burden to maintain another.
Update: You can use my bound package to do an improved version of this in a fully self-contained way!
Related
I'm trying to understand State newtype and I'm struggling with this explanation of the isomorphism in a book:
Newtypes must have the same underlying representation as the type they wrap, as the newtype wrapper disappears at compile time. So the function contained in the newtype must be isomorphic to the type it wraps. That is, there must be a way to go from the newtype to the thing it wraps and back again without losing information.
What does it mean applied to State newtype?
newtype State s a = State { runState :: s -> (a, s) }
That explanation "there must be a way to go from the newtype to the thing it wraps and back again" isn't clear.
Also, can you please say, where there is an isomorphism in this examples, where is not and why.
type Iso a b = (a -> b, b -> a)
newtype Sum a = Sum { getSum :: a }
sumIsIsomorphicWithItsContents :: Iso a (Sum a)
sumIsIsomorphicWithItsContents = (Sum, getSum)
(a -> Maybe b, b -> Maybe a)
[a] -> a, a -> [a]
The statement you quote makes no mention of State specifically. It is purely a statement about newtypes. It is a little misleading in referring to "the function contained in the newtype" because there is no requirement for the type wrapped by a newtype to be a function type - although this is the case for State and many other commonly used types defined by newtype.
The key thing for a newtype in general is exactly as it says: it has to simply wrap another type in a way that makes it trivial to go from the wrapped type to the wrapping one, and vice versa, with no loss of information - this is what it means for two types to be isomorphic, and also what makes it completely safe for the two types to have identical runtime representations.
It's easy to demonstrate typical data declarations that could not possibly fulfil this. For example take any type with 2 constructors, such as Either:
data Either a b = Left a | Right b
It's obvious that this is not isomorphic to either of its constituent types. For example, the Left constructor embeds a inside Either a b, but you can't get any of the Right values this way.
And even with a single constructor, if it takes more than one argument - such as the tuple constructor (,) - then again, you can embed either of the constituent types (given an arbitrary value of the other type) but you can't possibly get every value.
This is why the newtype keyword is only allowed for types with a single constructor which takes a single argument. This always provides an isomorphism, because given newtype Foo a = Foo a, then Foo constructor and the function \Foo a -> a are trivially inverses of each other. And this works the same for more complicated examples where the type constructor takes more type arguments, and/or where the wrapped type is more complex.
Such is exactly the case with State:
newtype State s a = State {runState :: s -> (a, s)}
The functions State and runState respectively wrap and unwrap the underlying type (which in this case is a function), and clearly are inverse to each other - therefore they provide an isomorphism.
Note finally that there is nothing special here about the use of record syntax in the definition - although it's very common in such cases in order to have an already-named "unwrapping" function. Other than this small convenience there is no difference from a newtype defined without record syntax.
To step back a little: newtype declarations are very similar to data declarations with a single constructor and a single argument - the difference is mainly in performance, as the keyword tells the compiler that the two types are equivalent so that there is no runtime overhead of conversion between the two types, which there otherwise would be. (There is also a difference with regard to laziness but I won't mention that, except here for completeness.) As for why do this rather than just use the underlying type - that's to provide extra type safety (there are 2 different types here for the compiler even though they're the same at runtime), and also allows typeclass instances to be specified without attaching those to the underlying type. Sum and Product are great examples here, as they provide Monoid instances for numeric types, based on addition and multiplication respectively, without giving either the undeserved distinction of being "the" Monoid instance for the underlying type.
And something similar is at work with State - when we use this type we signal explicitly that we're using it to represent state manipulation, which wouldn't be the case if we were just working with ordinary functions that happen to return a pair.
In python, I can add (union) and subtract (difference) sets with + and -. How would I set this up in Haskell? Would (-) = Data.Set.difference work? I tried it, but then I think regular subtraction with numbers got messed up.
Haskell places a few more restrictions on the overloading of numerical operators than Python does, there are rules and laws that must be followed in order to define them. For example, you would also need to define * and abs to go with it. Instead, use the operators already defined in Data.Set, namely \\ for set difference, and there isn't one already define for union, but you could easy make your own alias, or you could use it as
set1 `union` set2
I recommend sticking with the already defined functions and operators, it'll make your code much more readable to anyone else that takes a look at it. Feel free to introduce new operators that do more than just alias an existing function, although good practice says to do so sparingly still.
What you are proposing to do is, to put it a bit comically, very unhaskellic. Haskellers generally adopt the following attitude:
The same name or symbol should not be overloaded to mean two different things.
This means that all overloadable names or symbols (i.e., class operations) must have a consistent core meaning that all of their overloaded instances must respect.
In Haskell, the (+) and (-) operations are defined by the Num class. The docs aren't explicit about it, but to implement a class you must implement all of its methods, which includes things like fromInteger :: Num a => Integer -> a (the operation that converts any Integer into an instance of your class) and abs :: Num a => a -> a (take the absolute value of a number).
You can't implement the Num class for sets without profoundly abusing its meaning. So don't do it.
Note that there are other classes that may be more suitable to what you're trying to do. For example, there is the Monoid class that provides generic operations that are suitable for sets. In fact, the Data.Set module implements Monoid as union, so you can use the mappend function or (<>) operator to take the union of two sets generically (or the append of two lists, or many other things).
There is no obvious, popular class that the Set.difference operator would be an instance of, I'm afraid.
To define a Num instance for a type it would look like:
instance Num (Set a) where
(+) = -- definition
(-) = -- definition
-- etc
If you merely define, at the top level:
(-) = -- definition
Then you are simply shadowing the (-) that comes from Num.
As bheklilr says, Set is not a valid instance for Num because it cannot satisfy the ring laws. Haskell will not forbid you from defining the instance but it is a poor idea. People work with type classes by using their laws, so violating them results in incorrect programs.
For example, could you define a list in Haskell without defining a recursive structure? Or replace all lists by some function(s)?
data List a = Empty | (a, List a) -- <- recursive definition
EDIT
I gave the list as an example, but I was really asking about all data structures in general.
Maybe we only need one recursive data structure for all cases where recursion is needed? Like the Y combinator being the only recursive function needed. #TikhonJelvis 's answer made me think about that.
Now I'm pretty sure this post is better suited for cs.stackexchange.
About current selected answer
I was really looking for answers that looked more like the ones given by #DavidYoung & #TikhonJelvis, but they only give a partial answer and I appreciate them.
So, if any has an answer that uses functional concepts, please share.
That's a bit of an odd question. I think the answer is not really, but the definition of the data type does not have to be recursive directly.
Ultimately, lists are recursive data structures. You can't define them without having some sort of recursion somewhere. It's core to their essence.
However, we don't have to make the actual definition of List recursive. Instead, we can factor out recursion into a single data type Fix and then define all other recursive types with it. In a sense, Fix just captures the essence of what it means for a data structure to be recursive. (It's the type-level version of the fix function, which does the same thing for functions.)
data Fix f = Roll (f (Fix f))
The idea is that Fix f corresponds to f applied to itself repeatedly. To make it work with Haskell's algebraic data types, we have to throw in a Roll constructor at every level, but this does not change what the type represents.
Essentially, f applied to itself repeatedly like this is the essence of recursion.
Now we can define a non-recursive analog to List that takes an extra type argument f that replaces our earlier recursion:
data ListF a f = Empty | Cons a f
This is a straightforward data type that is not recursive.
If we combine the two, we get our old List type except with some extra Roll constructors at each recursive step.
type List a = Fix (ListF a)
A value of this type looks like this:
Roll (Cons 1 (Roll (Cons 2 (Roll Empty))))
It carries the same information as (Cons 1 (Cons 2 Empty)) or even just [1, 2], but a few extra constructors sprinkled through.
So if you were given Fix, you could define List without using recursion. But this isn't particularly special because, in a sense, Fix is recursion.
I'm not sure if all recursive structures can be replaced by a non-recursive version but some certainly can, including lists. One possible way to do this is with what is called a Boehm-Berarducci encoding. This a way to represent a structure as a function, specifically the fold over that structure (foldr in the case of a list):
{-# LANGUAGE RankNTypes #-}
type List a = forall x . (a -> x -> x) -> x -> x
-- ^^^^^^^^^^^^^ ^
-- Cons branch Nil branch
(From the above link with slightly different formatting)
This type is also something like a case analysis over the list. The first argument represents the cons case and the second argument represents the nil case.
In general, the branches of a sum type become different arguments to the function and fields of a product type become function types with an argument for each field. Note that in the encoding above, the nil branch is (in general) a non-function because the nil constructor takes no arguments, while the cons branch has two arguments since the cons constructor takes two arguments. The recursion parts of the definition are "replaced" with a Rank N type (called x here).
I think this question breaks down into considering three distinct feature subsets that Haskell provides:
Facilities for defining new data types.
A repertoire of built-in types.
A foreign function interface that allows interfacing with functionality external to the language.
Looking only at (1), the native type definition facilities don't really provide for defining any infinitely-large types other than by recursion.
Looking at (2), however, Haskell 2010 provides the Data.Array module, which provides array types that together with (1) can be used to build non-recursive definitions of many different structures.
And even if the language did not provide arrays, (3) means that we could bolt them to the language as an FFI extension. Haskell implementations are also allowed to provide extra functionality that can be used for this in stead of the FFI, and many libraries for GHC exploit those (e.g., vector).
So I'd say that the best answer is that Haskell only allows you to define nonrecursive collection types only to the extent that it provides you with basic built-in ones that you can use as building blocks for more complex ones.
First of all, I want to clarify that I've tried to find a solution to my problem googling but I didn't succeed.
I need a way to compare two expressions. The problem is that these expressions are not comparable. I'm coming from Erlang, where I can do :
case exp1 of
exp2 -> ...
where exp1 and exp2 are bound. But Haskell doesn't allow me to do this. However, in Haskell I could compare using ==. Unfortunately, their type is not member of the class Eq. Of course, both expressions are unknown until runtime, so I can't write a static pattern in the source code.
How could compare this two expressions without having to define my own comparison function? I suppose that pattern matching could be used here in some way (as in Erlang), but I don't know how.
Edit
I think that explaining my goal could help to understand the problem.
I'm modyfing an Abstract Syntax Tree (AST). I am going to apply a set of rules that are going to modify this AST, but I want to store the modifications in a list. The elements of this list should be a tuple with the original piece of the AST and its modification. So the last step is to for each tuple search for a piece of the AST that is exactly the same, and substitute it by the second element of the tuple. So, I will need something like this:
change (old,new):t piece_of_ast =
case piece_of_ast of
old -> new
_ -> piece_of_ast
I hope this explanation clarify my problem.
Thanks in advance
It's probably an oversight in the library (but maybe I'm missing a subtle reason why Eq is a bad idea!) and I would contact the maintainer to get the needed Eq instances added in.
But for the record and the meantime, here's what you can do if the type you want to compare for equality doesn't have an instance for Eq, but does have one for Data - as is the case in your question.
The Data.Generics.Twins package offers a generic version of equality, with type
geq :: Data a => a -> a -> Bool
As the documentation states, this is 'Generic equality: an alternative to "deriving Eq" '. It works by comparing the toplevel constructors and if they are the same continues on to the subterms.
Since the Data class inherits from Typeable, you could even write a function like
veryGenericEq :: (Data a, Data b) => a -> b -> Bool
veryGenericEq a b = case (cast a) of
Nothing -> False
Maybe a' -> geq a' b
but I'm not sure this is a good idea - it certainly is unhaskelly, smashing all types into one big happy universe :-)
If you don't have a Data instance either, but the data type is simple enough that comparing for equality is 100% straightforward then StandaloneDeriving is the way to go, as #ChristianConkle indicates. To do this you need to add a {-# LANGUAGE StandaloneDeriving #-} pragma at the top of your file and add a number of clauses
deriving instance Eq a => Eq (CStatement a)
one for each type CStatement uses that doesn't have an Eq instance, like CAttribute. GHC will complain about each one you need, so you don't have to trawl through the source.
This will create a bunch of so-called 'orphan instances.' Normally, an instance like instance C T where will be defined in either the module that defines C or the module that defines T. Your instances are 'orphans' because they're separated from their 'parents.' Orphan instances can be bad because you might start using a new library which also has those instances defined; which instance should the compiler use? There's a little note on the Haskell wiki about this issue. If you're not publishing a library for others to use, it's fine; it's your problem and you can deal with it. It's also fine for testing; if you can implement Eq, then the library maintainer can probably include deriving Eq in the library itself, solving your problem.
I'm not familiar with Erlang, but Haskell does not assume that all expressions can be compared for equality. Consider, for instance, undefined == undefined or undefined == let x = x in x.
Equality testing with (==) is an operation on values. Values of some types are simply not comparable. Consider, for instance, two values of type IO String: return "s" and getLine. Are they equal? What if you run the program and type "s"?
On the other hand, consider this:
f :: IO Bool
f = do
x <- return "s" -- note that using return like this is pointless
y <- getLine
return (x == y) -- both x and y have type String.
It's not clear what you're looking for. Pattern matching may be the answer, but if you're using a library type that's not an instance of Eq, the likely answer is that comparing two values is actually impossible (or that the library author has for some reason decided to impose that restriction).
What types, specifically, are you trying to compare?
Edit: As a commenter mentioned, you can also compare using Data. I don't know if that is easier for you in this situation, but to me it is unidiomatic; a hack.
You also asked why Haskell can't do "this sort of thing" automatically. There are a few reasons. In part it's historical; in part it's that, with deriving, Eq satisfies most needs.
But there's also an important principle in play: the public interface of a type ideally represents what you can actually do with values of that type. Your specific type is a bad example, because it exposes all its constructors, and it really looks like there should be an Eq instance for it. But there are many other libraries that do not expose the implementation of their types, and instead require you to use provided functions (and class instances). Haskell allows that abstraction; a library author can rely on the fact that a consumer can't muck about with implementation details (at least without using unsafe tools like unsafeCoerce).
A list in Haskell might look like this:
data List a = Nil | Cons a (List a)
A type theoretic interpretation is:
λα.μβ.1+αβ
which encodes the list type as the fixed point of a functor. In Haskell this could be represented:
data Fix f = In (f (Fix f))
data ListF a b = Nil | Cons a b
type List a = Fix (ListF a)
I'm curious about the scope of the earlier μ binder. Can a name bound in an outer scope remain available in an inner scope? Is, say, the following a valid expression:
μγ.1+(μβ.1+γβ)γ
...perhaps it's the same as:
μβ.μγ.1+(1+γβ)γ
...but then how would things change when the name is reused:
μβ.μγ.1+(μβ.1+γβ)γ
Are the above all regular types?
Scoping of μ works no different from other binders, so yes, all your examples are valid. They are also regular, because they do not even contain a λ. (*)
As for equality, that depends on what sort of μ-types you have. There are basically two different notions:
equi-recursive: in that case, the typing rules assume an equivalence
μα.T = T[μα.T / α]
i.e., a recursive type is considered equal to its one-level 'unrolling', where the μ is removed and the μ-bound variable is replaced by the type itself (and because this rule can be applied repeatedly, one can unroll arbitrary many times).
iso-recursive: here, no such equivalence exists. Instead, a μ-type is a separate form of type with its own expression forms to introduce and eliminate it -- they are usually called roll and unroll (or fold and unfold), and are typed as follows:
roll : T[μα.T / α] → μα.T
unroll : μα.T → T[μα.T / α]
These must be applied explicitly on the term level to mirror the equation above (once for each level of unrolling).
Functional languages like ML or Haskell usually use the latter for their interpretation of datatypes. However, the roll/unroll is built into the use of the data constructors. So each constructor is an injection into an iso-recursive type composed with an injection into a sum type (and inversely when matched).
Your examples are all different under the iso-recursive interpretation. The first and the third are the same under an equi-recursive interpretation, because the outer μ just disappears when you apply the above equivalence.
(*) Edit: An irregular recursive type is one whose infinite expansion does not correspond to a regular tree (or equivalently, can not be represented by a finite cyclic graph). Such a case can only be expressed with recursive type constructors, i.e., a λ that occurs under a μ. For example, μα.λβ.1+α(β×β) -- corresponding to the recursive equation t(β) = 1+t(β×β) -- would be irregular, because the recursive type constructor α is recursively applied to a type "larger" than its argument, and hence every application is a recursive type that "grows" indefinitely (and consequently, you cannot draw it as a graph).
It's worth noting, however, that in most type theories with μ, its bound variable is restricted to ground kind, so cannot express irregular types at all. In particular, a theory with unrestricted equi-recursive type constructors would have non-terminating type normalisation, so type equivalence (and thus type checking) would be undecidable. For iso-recursive types you'd need higher-order roll/unroll, which is possible, but I'm not aware of much literature investigating it.
Your μ type expressions are valid. I believe your types are regular as well since you only use recursion, sum, and products.
The type
T1 = μγ.1+(μβ.1+γβ)γ
does not look equal to
T2 = μβ.μγ.1+(1+γβ)γ
since inr (inr (inl *, inr (inl *)), inl *) has the second type but not the first.
The last type
T3 = μβ.μγ.1+(μβ.1+γβ)γ
is equal to (α-converting the first β)
μ_.μγ.1+(μβ.1+γβ)γ
which is, unfolding the top-level μ,
μγ.1+(μβ.1+γβ)γ
which is T1.
Basically, the scope of μ-bound variables follows the same rules of λ-bound variables. That is, the value of each occurrence of a variable β is provided by the closest μβ on top of it.