I have a rough idea about what this is but if someone has an explanation of the 'expression problem' that they think is succinct and intuitive I would love to hear it.
Watch this lecture.
The idea is that your program is a combination of a datatype and operations over it. The problem asks for an implementation that allows to add new cases of the type and new operations without the need for recompilation of the old modules and keeping static type safety(no casts or runtime type checks).
It's interesting to notice that in functional programming languages it's easy to add new operations, but hard to add cases to the datatype. While in an OO language it's the other way round. This is one of the big conceptual differences between the two programming paradigms.
The idea behind the problem is that text is 1 dimensional. Even if you have lines and columns, you generally read it, word by word, line by line. So does the compiler.
And you try to represent some kind of 2 or more dimensional data in it. For example a table in row-mayor order looks like this:
((A, B, C), (D, E, F), (G, H, I))
In this representation, it's quite easy to add a new row at the end, without touching the rest:
((A, B, C), (D, E, F), (G, H, I), (J, K, L))
But adding columns is problematic a bit, you need to touch it 4 different places:
((A, B, C, M), (D, E, F, N), (G, H, I, O), (J, K, L, P))
You generally run into this problem in practice, when dealing with abstract classes: it's quite easy to add a new subtype as a new module, but when you add a new abstract method, you'll need to touch all the modules and add it; you need to do the same thing in many places. Normally you make abstractions to protect against these repetitive things.
There is no solution to this problem as long as you use 1D representation.
The solution to this problem would be an editor that can let you edit these table like things like a real table and not like text (in an Excel like view, where you can conveniently add new columns and rows).
There is also this article about solving the problem with Clojure, however the problem is presented in Java so it should make sense even if you don't know Clojure, especially with the help of the little charts.
which says:
Philip Wadler of Bell Labs coined the term Expression Problem in an unpublished paper that he circulated by email in 1998. As he put it, "The Expression Problem is a new name for an old problem. The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts)."
Related
I'm new to F# and Haskell and am implementing a project in order to determine which language I would prefer to devote more time to.
I have a numerous situations where I expect a given numerical type to have given dimensions based on parameters given to a top-level function (ie, at runtime). For example, in this F# snippet, I have
type DataStreamItem = LinearAlgebra.Vector<float32>
type Ball =
{R : float32;
X : DataStreamItem}
and I expect all instances of type DataStreamItem to have D dimensions.
My question is in the interests of algorithm development and debugging since such shape-mismatche-bugs can be a headache to pin down but should be a non-issue when the algorithm is up-and-running:
Is there a way, in either F# or Haskell, to constrain DataStreamItem and / or Ball to have dimensions of D? Or do I need to resort to pattern matching on every calculation?
If the latter is the case, are there any good, light-weight paradigms to catch such constraint violations as soon as they occur (and that can be removed when performance is critical)?
Edit:
To clarify the sense in which D is constrained:
D is defined such that if you expressed the algorithm of the function main(DataStream) as a computation graph, all of the intermediate calculations would depend on the dimension of D for the execution of main(DataStream). The simplest example I can think of would be a dot-product of M with DataStreamItem: the dimension of DataStream would determine the creation of dimension parameters of M
Another Edit:
A week later, I find the following blog outlining precisely what I was looking for in dependant types in Haskell:
https://blog.jle.im/entry/practical-dependent-types-in-haskell-1.html
And Another:
This reddit contains some discussion on Dependent Types in Haskell and contains a link to the quite interesting dissertation proposal of R. Eisenberg.
Neither Haskell not F# type system is rich enough to (directly) express statements of the sort "N nested instances of a recursive type T, where N is between 2 and 6" or "a string of characters exactly 6 long". Not in those exact terms, at least.
I mean, sure, you can always express such a 6-long string type as type String6 = String6 of char*char*char*char*char*char or some variant of the sort (which technically should be enough for your particular example with vectors, unless you're not telling us the whole example), but you can't say something like type String6 = s:string{s.Length=6} and, more importantly, you can't define functions of the form concat: String<n> -> String<m> -> String<n+m>, where n and m represent string lengths.
But you're not the first person asking this question. This research direction does exist, and is called "dependent types", and I can express the gist of it most generally as "having higher-order, more powerful operations on types" (as opposed to just union and intersection, as we have in ML languages) - notice how in the example above I parametrize the type String with a number, not another type, and then do arithmetic on that number.
The most prominent language prototypes (that I know of) in this direction are Agda, Idris, F*, and Coq (not really the full deal AFAIK). Check them out, but beware: this is kind of the edge of tomorrow, and I wouldn't advise starting a big project based on those languages.
(edit: apparently you can do certain tricks in Haskell to simulate dependent types, but it's not very convenient, and you have to enable UndecidableInstances)
Alternatively, you could go with a weaker solution of doing the checks at runtime. The general gist is: wrap your vector types in a plain wrapper, don't allow direct construction of it, but provide constructor functions instead, and make those constructor functions ensure the desired property (i.e. length). Something like:
type Stream4 = private Stream4 of DataStreamItem
with
static member create (item: DataStreamItem) =
if item.Length = 4 then Some (Stream4 item)
else None
// Alternatively:
if item.Length <> 4 then failwith "Expected a 4-long vector."
item
Here is a fuller explanation of the approach from Scott Wlaschin: constrained strings.
So if I understood correctly, you're actually not doing any type-level arithmetic, you just have a “length tag” that's shared in a chain of function calls.
This has long been possible to do in Haskell; one way that I consider quite elegant is to annotate your arrays with a standard fixed-length type of the desired length:
newtype FixVect v s = FixVect { getFixVect :: VU.Vector s }
To ensure the correct length, you only provide (polymorphic) smart constructors that construct from the fixed-length type – perfectly safe, though the actual dimension number is nowhere mentioned!
class VectorSpace v => FiniteDimensional v where
asFixVect :: v -> FixVect v (Scalar v)
instance FiniteDimensional Float where
asFixVect s = FixVect $ VU.singleton s
instance (FiniteDimensional a, FiniteDimensional b, Scalar a ~ Scalar b) => FiniteDimensional (a,b) where
asFixVect (a,b) = case (asFixVect a, asFixVect b) of
(FixVect av, FixVect bv) -> FixVect $ av<>bv
This construction from unboxed tuples is really inefficient, however this doesn't mean you can write efficient programs with this paradigm – if the dimension always stays constant, you only need to wrap and unwrap the once and can do all the critical operations through safe yet runtime-unchecked zips, folds and LA combinations.
Regardless, this approach isn't really widely used. Perhaps the single constant dimension is in fact too limiting for most relevant operations, and if you need to unwrap to tuples often it's way too inefficient. Another approach that is taking off these days is to actually tag the vectors with type-level numbers. Such numbers have become available in a usable form with the introduction of data kinds in GHC-7.4. Up until now, they're still rather unwieldy and not fit for proper arithmetic, but the upcoming 8.0 will greatly improve many aspects of this dependently-typed programming in Haskell.
A library that offers efficient length-indexed arrays is linear.
In learning Haskell I have set a puzzle for myself which is to write a 4 X 4 KenKen solver. This involves placing the number 1 to 4 in a 4 by 4 matrix such that each row and column contains distinct values. There are also cages which constrain the numbers contained within based on addition, multiplication, division (two cells only) or subtraction (two cells only). For full rules see wikipedia page on KenKen.
I've been programming imperatively for 30 years and I'm wondering how to approach this in Haskell. It's here that I need some advice as an absolute beginner.
My sketchy overview :-
for the moment, just hardcode the constraints and any starting numbers to avoid having to learn how to do IO and parsing,
keep it at 4 by 4 for the moment to make life simplier,
treat the cells as an immutable list of Maybe int with length 16,
create a typeclass for constraint which can take a list of cells and return a boolean for if the constranit if violated or not (or is this a bit OO and I should create functions for these?),
a constraint would say something like cells in positions 1, 2 and 5 must add up to 6. A function would take a list of cells it would return true or false,
write constraints for plus, minus, divide, multiply and unique,
create a recursive solve function that takes in the cells and a list of constraints,
solve will check the cells against each of the constraints and call itself with new list trying out a different number or backtrack
So my question is, does this approach seems like it would work and is it idomatic in Haskell? Can you suggest how I could do something more functional without needing to know anything too advanced. I'm not interested in this being the most performant way to solve this problem since it's just an learning exercise I set myself.
EDIT: The answer and comments below centre around the need for the type class so that's probably the bit I'm getting wrong. I feel the need for it so I can treat all the different sorts of constraints polymorphically. I believe what everyone is saying is that this is insufficient and I can just have a list of functions that accept a list of cells and return the boolean.
That mostly sounds ok, but you are right about constraints being functions not a typeclass. In haskell the job of an interface is split in two. Typeclasses handle the this function can take any type that implements this interface part of the equation. An example of this is the Show typeclass. By implementing the Show type class your data type supports the show (to string) function. This means that print will now print instances of your type.
The part of the interface concept where you implement an interface so that you can have multiple implementations of the same function is handled through higher order functions rather than identically named functions attached to different types. As an example of this doFunc opp a b = opp a b it will have the type (a -> b -> c) -> a -> b -> c. Then this function can take any two operand function and apply it like doFunc (+) 1 2 or doFunc (*) 1 2.
The main thing I would suggest is try working bottom up. I found it really helped with the build small functions and compose them philosophy of FP programming.
I've read about some of the issues with Haskell records, in particular, the fact that two elements in the same module can not have the same name.
I understand you can work around this by having separate modules, but I didn't want to do that and instead tried this approach:
class HasX a where
x :: a -> X
data D1 = D1 { d1_x :: X, ... }
instance HasX D1 where
x = d1_x
data D2 = D2 { d2_x :: X, ... }
instance HasX D2 where
x = d2_x
(This only does gets, not sets, I'd of course need to write more code to do sets).
However, it seems the class and instance declarations for all this seem like a like of boilerplate which should be able to be eliminated, using template haskell or something else.
Is there a library or extension to GHC that makes such an approach less messy to write?
It seems Data.Has encapsulates a lot of what you're looking for. In their vocabulary they I think that their Knows type-class is closer to your Has, and it also provides a signature for injection as well.
They also use a labeling mechanism to treat an issue that I don't think you've considered yet: records which contain fields that have the same type. They use type-level labels to do this disambiguation.
For convenience, there also seems to be some support that provides a generator for the Has instance with template Haskell in Has-TH
You can find more type-level labels and other record-relevant material in works of the Oleg the Type Magician, like OOHaskell (also with Ralf Lämmel).
data-accessor-template may at least help to write the set/get accessors. Maybe some people can come up with the Template Haskell code for generation of classes and instances for every record field name.
However, I do not use the Template Haskell stuff myself. It restricts you to GHC, and even to specific GHC versions, since Template Haskell changes between different GHC versions. Having one (major) record per module really pays off.
Short and sweet: I've seen several sources talking about "supercompilation". But I have yet to find one single document anywhere on the face of the Internet which describes what this is. Presumably because it seems simple enough to whoever that it isn't even worth explaining.
Does anybody know what this actually is?
Supercompilation can be approached as a generalization of partial evaluation. The idea behind partial evaluation is that many parts of a program can be evaluated at compile time, and so should be. Supercompilation extends this, evaluating things that can't be fully done at compile time as well, like turning map f (map g xs) into map (f . g) xs without anything besides the definition of map (At least I think I got partial evaluation right - I've only read much about supercompilation).
Another way to look at it is as a combination of many other optimizations, like deforestation, specialization, and inlining. By acting as if it already knew the inputs to functions and evaluating, it can get a more direct method of calculating the result - it can get rid of intermediate data structures by seeing how they will be used or it can plug in all possible values and then wrap the result in a case, or do something else with its pretend values.
Max Bolingbroke has a number of useful papers on the subject - I recommend the first one, Supercompilation by Evaluation, as an introduction. Section 2 introduces the subject by example and the rest, while a bit difficult to get through, is very informative about the process. Neil Mitchell also has a number of good presentations describing it.
I hope that helps.
From Wikipedia on Metacompilation:
Metacompilation is a computation which involves metasystem transitions
(MST) from a computing machine M to a metamachine M' which controls,
analyzes and imitates the work of M. Semantics-based program
transformation, such as partial evaluation and supercompilation (SCP),
is metacomputation.
More about Metasystems on Wikipedia.
I am not knowledgeable on the subject, but I'll give my understanding of the description. Say we had a simple program that could copy stdin to stdout. This would be our computing machine M. Our metamachine M' is a second program that takes the source of M as input (or is otherwise constructed to inherently know of M) and is therefore able to understand not only what M does, but how it does so.
If my understanding is correct, then the obvious question is why do we care about M'? What comes to my mind is automatic optimisations. If we can understand both how M works and what M is trying to accomplish, M' may solve ways to improve the operation of M, either in space or time. Furthermore, and importantly, M' may substitute M since M' can accomplish whatever M did. This means that M'' can improve on the ways M' optimised M, and subsequently replace M', and so on.
Most of the problems I have to solve in my job as a developer have to do with data modeling.
For example in a OOP Web Application world I often have to change the data properties that are in a object to meet new requirements.
If I'm lucky I don't even need to programmatically add new "behavior" code (functions,methods). Instead I can declarative add validation and even UI options by annotating the property (Java).
In Functional Programming it seems that adding new data properties requires lots of code changes because of pattern matching and data constructors (Haskell, ML).
How do I minimize this problem?
This seems to be a recognized problem as Xavier Leroy states nicely on page 24 of "Objects and Classes vs. Modules"
- To summarize for those that don't have a PostScript viewer it basically says FP languages are better than OOP languages for adding new behavior over data objects but OOP languages are better for adding new data objects/properties.
Are there any design pattern used in FP languages to help mitigate this problem?
I have read Phillip Wadler's recommendation of using Monads to help this modularity problem but I'm not sure I understand how?
As Darius Bacon noted, this is essentially the expression problem, a long-standing issue with no universally accepted solution. The lack of a best-of-both-worlds approach doesn't stop us from sometimes wanting to go one way or the other, though. Now, you asked for a "design pattern for functional languages", so let's take a shot at it. The example that follows is written in Haskell, but isn't necessarily idiomatic for Haskell (or any other language).
First, a quick review of the "expression problem". Consider the following algebraic data type:
data Expr a = Lit a | Sum (Expr a) (Expr a)
exprEval (Lit x) = x
exprEval (Sum x y) = exprEval x + exprEval y
exprShow (Lit x) = show x
exprShow (Sum x y) = unwords ["(", exprShow x, " + ", exprShow y, ")"]
This represents simple mathematical expressions, containing only literal values and addition. With the functions we have here, we can take an expression and evaluate it, or show it as a String. Now, say we want to add a new function--say, map a function over all the literal values:
exprMap f (Lit x) = Lit (f x)
exprMap f (Sum x y) = Sum (exprMap f x) (exprMap f y)
Easy! We can keep writing functions all day without breaking a sweat! Algebraic data types are awesome!
In fact, they're so awesome, we want to make our expression type more, errh, expressive. Let's extend it to support multiplication, we'll just... uhh... oh dear, that's going to be awkward, isn't it? We have to modify every function we just wrote. Despair!
In fact, maybe extending the expressions themselves is more interesting than adding functions that use them. So, let's say we're willing to make the trade-off in the other direction. How might we do that?
Well, no sense doing things halfway. Let's up-end everything and invert the whole program. What does that mean? Well, this is functional programming, and what's more functional than higher-order functions? What we'll do is replace the data type representing expression values with one representing actions on the expression. Instead of choosing a constructor we'll need a record of all possible actions, something like this:
data Actions a = Actions {
actEval :: a,
actMap :: (a -> a) -> Actions a }
So how do we create an expression without a data type? Well, our functions are data now, so I guess our data needs to be functions. We'll make "constructors" using regular functions, returning a record of actions:
mkLit x = Actions x (\f -> mkLit (f x))
mkSum x y = Actions
(actEval x + actEval y)
(\f -> mkSum (actMap x f) (actMap y f))
Can we add multiplication more easily now? Sure can!
mkProd x y = Actions
(actEval x * actEval y)
(\f -> mkProd (actMap x f) (actMap y f))
Oh, but wait--we forgot to add an actShow action earlier, let's add that in, we'll just... errh, well.
At any rate, what does it look like to use the two different styles?
expr1plus1 = Sum (Lit 1) (Lit 1)
action1plus1 = mkSum (mkLit 1) (mkLit 1)
action1times1 = mkProd (mkLit 1) (mkLit 1)
Pretty much the same, when you're not extending them.
As an interesting side note, consider that in the "actions" style, the actual values in the expression are completely hidden--the actEval field only promises to give us something of the correct type, how it provides it is its own business. Thanks to lazy evaluation, the contents of the field may even be an elaborate computation, performed only on demand. An Actions a value is completely opaque to external inspection, presenting only the defined actions to the outside world.
This programming style--replacing simple data with a bundle of "actions" while hiding the actual implementation details in a black box, using constructor-like functions to build new bits of data, being able to interchange very different "values" with the same set of "actions", and so on--is interesting. There's probably a name for it, but I can't quite seem to recall...
I've heard this complaint more than a few times, and it always confuses me. The questioner wrote:
In Functional Programming it seems
that adding new data properties
requires lots of code changes because
of pattern matching and data
constructors (Haskell, ML).
But this is by and large a feature, and not a bug! When you change the possibilities in a variant, for example, the code that accesses that variant via pattern-matching is forced to consider the fact that new possibilities have arisen. This is useful, because indeed you do need to consider whether that code needs to change to react to the semantic changes in the types it manipulates.
I would argue with the claim that "lots of code changes" are required. With well-written code, the type system usually does an impressively good job of bringing to the fore the code that needs to be considered, and not much more.
Perhaps the problem here is that it's hard to answer the question without a more concrete example. Consider providing a piece of code in Haskell or ML that you're not sure how to evolve cleanly. I imagine you'll get more precise and useful answers that way.
This tradeoff is known in the programming-language-theory literature as the expression problem:
The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).
Solutions have been put forward, but I haven't studied them. (Much discussion at Lambda The Ultimate.)
In Haskell at least I would make an abstract data type. That is create a type that doesn't export constructors. Users of the type loose the ability to pattern match on the type and you have to provide functions for working with the type. In return you get a type that is easier to modify without changing code written by users of the type.
If the new data imply no new behaviour, as in an application where we are asked to add a "birthdate" field to a "person" resource and then all we have to do is to add it to a list of fields that are part of the person resource, then it's easy to solve in both the functional and OOP world. Just don't treat the "birthdate" as part of your code; it's just part of your data.
Let me explain: if the birthdate is something that implies a different application behaviour, e.g. that we do something differently if the person is underage, then in OOP we would add a birthdate field to the person class, and in FP we would add similarly a birthdate field to a person data structure.
If there is no behaviour attached to "birthdate" then there should be no field named "birthdate" in the code. A data structure such as a dictionary (a map) would hold the various fields. Adding a new one would require no program changes, no matter if you it's OOP or FP. Validations would be added similarly, by attaching a validation regexp or using a similar validation little language to express in data what the validation behaviour should be.