
Hi, I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures? For example, take a blog. Is the first step likely to be something like: data BlogEntry = BlogEntry { title::String,content::String,comments::[Comment] } type Blog = [BlogEntry] or more likely thinking about what functions will be required: addEntry :: BlogEntry -> Blog -> Blog displayBlog :: Blog -> HTML displayEntry :: BlogEntry -> HTML I'm trying to get my brain out of thinking OO thinking more functionally. Thanks, Levi lstephen.wordpress.com

Hello Levi, Tuesday, November 6, 2007, 3:02:49 AM, you wrote:
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
not first nor second. i write function bodies, then declare appropriate data types when required and don't write function types at all from my POV, FP allows to concentrate on the algorithms and mostly ignore all auxiliary definitions -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On 11/5/07, Levi Stephen
Hi,
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
For example, take a blog. Is the first step likely to be something like:
data BlogEntry = BlogEntry { title::String,content::String,comments::[Comment] } type Blog = [BlogEntry]
or more likely thinking about what functions will be required:
addEntry :: BlogEntry -> Blog -> Blog displayBlog :: Blog -> HTML displayEntry :: BlogEntry -> HTML
I don't know how you can do one without the other. I would probably start in a case like this by writing down a few basic types, then trying to start to write down the function type signatures, which will probably point to more types that need to be defined, iterating until I reach a fixed point. I can't write type signatures unless I know what the types are. But I don't know what the types should look like unless I know what the interface should look like, either. One thing that's for sure, though -- I always write type signatures for top-level definitions. I can't think straight unless I write the type signature before writing the code. Cheers, Tim -- Tim Chevalier * catamorphism.org * Often in error, never in doubt "Thus spake the Master Programmer: When you have learned to snatch the error code from the trap frame, it will be time for you to leave."--J. Geoffrey

On Tue, 2007-11-06 at 10:32 +1030, Levi Stephen wrote:
Hi,
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
For example, take a blog. Is the first step likely to be something like:
data BlogEntry = BlogEntry { title::String,content::String,comments::[Comment] } type Blog = [BlogEntry]
or more likely thinking about what functions will be required:
addEntry :: BlogEntry -> Blog -> Blog displayBlog :: Blog -> HTML displayEntry :: BlogEntry -> HTML
I'm trying to get my brain out of thinking OO thinking more functionally.
I typically start with a list of the types I want, then the minimal list of type signatures. Ideally, it should be possible to write an arbitrarily large set of programs in the given application domain by composing this initial list of functions (so the data type can be specified abstractly, if desired). Then I define the bodies of the types, then I find out that I can't define the functions elegantly, so I start over from the top. Once my function definitions are natural, I know I'm done, so I can start writing derived functions (key insight: most functions in FP don't need access to the representation of your data types. This is the counter-part, if you will, of encapsulation in OO: well-encapsulated types are those where most code can be written by composing a short list of primitives with direct access to the type.) jcc

I typically start with a list of the types I want, then the minimal list of type signatures. Ideally, it should be possible to write an arbitrarily large set of programs in the given application domain by composing this initial list of functions (so the data type can be specified abstractly, if desired).
And if we translate that paragraph into, say, Erlang, what will we get? It seems that we will get a lot of tests instead of types. Or a lot of (pre/post)predicates about values.

Levi Stephen wrote:
Hi,
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
For example, take a blog. Is the first step likely to be something like:
data BlogEntry = BlogEntry { title::String,content::String,comments::[Comment] } type Blog = [BlogEntry]
or more likely thinking about what functions will be required:
addEntry :: BlogEntry -> Blog -> Blog displayBlog :: Blog -> HTML displayEntry :: BlogEntry -> HTML
I think you can work either way around, or both, or a mixture. If I had to choose between the two, I would *definitely* choose the second. Writing a program is like defining a custom language. Start by defining your primitives (functions). The data types are just "whatever you need to make it work". YMMV :) Jules

Levi Stephen wrote:
Hi,
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
In *any* programming language, my workflow usually goes like this: 1. Think about it for a minute or two. Try to decide on good abstractions. 2. Start programming it. 3. Realise that my chosen system of abstractions is fundamentally flawed. 4. Delete source code. 5. Think some more. 6. Start coding again using new plan. 7. Iterate as required. I'm not sure whether you can properly call that "design" though... more like "iterative prototyping". ;-) It used to be easier with OOP. I'm still not quite sure how to pick the best possible abstractions in an FP context...

Hello Andrew, Tuesday, November 6, 2007, 9:34:34 PM, you wrote:
It used to be easier with OOP. I'm still not quite sure how to pick the best possible abstractions in an FP context...
for me, abstraction is anything that i want to be an abstraction. i just write code in the close-to-natural language and it becomes Haskell program when appropriate syntax applied. say, i want to display archive listing. i write: dir <- readDir archive listing = map listfile dir print$ [header]++listing++[footer] then i write definitions of readDir, listfile and so on in the same manner -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat Ziganshin wrote:
Hello Andrew,
Tuesday, November 6, 2007, 9:34:34 PM, you wrote:
It used to be easier with OOP. I'm still not quite sure how to pick the best possible abstractions in an FP context...
for me, abstraction is anything that i want to be an abstraction. i just write code in the close-to-natural language and it becomes Haskell program when appropriate syntax applied.
Well, in my experience, figuring out just the right abstractions to use is the difference between a huge pile of code that really doesn't quite work right, and a much smaller, simpler, more capable program. Unfortunately, figuring out the best way to divide up a given problem usually requires a lot of trial and error. (I was just starting to get good at it in OOP... now I'm back at the bottom. Heh!)

Hello Andrew, Tuesday, November 6, 2007, 10:55:58 PM, you wrote:
for me, abstraction is anything that i want to be an abstraction. i just write code in the close-to-natural language and it becomes Haskell program when appropriate syntax applied.
Well, in my experience, figuring out just the right abstractions to use
i don't think about abstractions, just using top-down approach. for me, FP benefit is that when you see that some two things are similar - you can factor out this similarity. in OOP, you should translate it into some class interface, in Haskell you just define parameterized code/data and it works. selection of good abstractions based on these two criteria: 1) factoring out common parts and 2) existence of natural description of the factored part. if i don't see natural description, i can slightly change the factored part -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat Ziganshin wrote:
Hello Andrew,
Tuesday, November 6, 2007, 10:55:58 PM, you wrote:
for me, abstraction is anything that i want to be an abstraction. i just write code in the close-to-natural language and it becomes Haskell program when appropriate syntax applied.
Well, in my experience, figuring out just the right abstractions to use
i don't think about abstractions, just using top-down approach. for me, FP benefit is that when you see that some two things are similar - you can factor out this similarity. in OOP, you should translate it into some class interface, in Haskell you just define parameterized code/data and it works. selection of good abstractions based on these two criteria: 1) factoring out common parts and 2) existence of natural description of the factored part. if i don't see natural description, i can slightly change the factored part
This thread sums up some of my thoughts pretty well. I'm coming from OOP where I was getting comfortable and was confident of spotting appropriate abstractions. Now I have to learn how to select the appropriate abstractions in Haskell. e.g., selecting between a variant type or type class is often a tricky one for me. It is good to hear that people are having success with the code, refactor duplication, repeat process. I have used this in OOP as well and the path it takes is interesting to compare with initial design thoughts. Again, it's just choosing the best way to remove this duplication :) Levi

Hello Levi, Thursday, November 8, 2007, 2:06:14 AM, you wrote:
Now I have to learn how to select the appropriate abstractions in Haskell. e.g., selecting between a variant type or type class is often a tricky one for me.
that's easy part. i never have need to use type classes in application code, only in libraries. type class allows extending initial behavior in other modules. when you need it - go to use typeclasses -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Tue, 2007-11-06 at 10:32 +1030, Levi Stephen wrote:
Hi,
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
As others have mentioned: both. But there's a third thing that you can do, other than start implementing: think about the laws/properties that should hold. That's not always simple, in fact, it rarely is. But it helps you get one step closer to what you need to implement. If you can find useful properties, though, you get lots of test-cases for free by formulating and testing them using QuickCheck. Then, when implementing, you probably realize that you might need some more combinators. I usually then stop and implement the combinator first, test it and continue. Some other guidelines I recommend: * Try not to be too abstract in the first place. Try implementing a specialized version first, and then see how you can abstract it, once you implemented all facets. Sometimes, you need to add some instance constraints, but the compiler will tell you where. * Test early and often. Note that in Haskell you can get the compiler to typecheck successfully by adding dummy-definitions using "_" and "undefined". E.g.: foo :: A -> B -> C foo _ _ = undefined * Define abstract types, or at least type synonyms. It's so easy to define new abstract types in Haskell, that you should do it often. This makes it much easier to later choose the proper data structure, once you know what details you need. Also, it's implicit documentation, compare type Author = String; type Title = String addPost :: Author -> Title -> String -> Blog -> Blog and addPost :: String -> String -> String -> Blog -> Blog * Think about complexity of your primitives. This is hard to fix later, so it should be taken into account early on. Of course, there's sometimes a fine line between mature and premature optimizations. * As Don often says, try to keep things pure wherever possible. It will make your code so much more compositional, easier to test and correct. There's more, but I'll stop here for now...

Thomas Schilling wrote:
Levi Stephen wrote:
I'm was wondering how most people work during when designing a functional program. Do you create data structures/types first? Do you work from some type signatures?
But there's a third thing that you can do, other than start implementing: think about the laws/properties that should hold. That's not always simple, in fact, it rarely is.
Yes, the classic approach: systematically derive programs from their specification. The classic paper on that is Paul Hudak. The Design of a Pretty-printing Library. http://citeseer.ist.psu.edu/hughes95design.html with a follow-up Philip Wadler. A prettier printer. http://decenturl.com/homepages.inf.ed/wadler-98-prettier-printer The man who derives all his programs from specification is Richard Bird. You may want to have a look at his recent sudoku solver Richard Bird. A program to solve Sudoku. Slides: http://icfp06.cs.uchicago.edu/bird-talk.pdf where he starts with an apparently correct but hopelessly slow specification and transforms it into a blazingly fast one. His introduction to Haskell Richard Bird. Introduction to Functional Programming using Haskel (2nd edition). http://decenturl.com/amazon/bird-introduction-functional emphasizes the classic style, too. You may think "this is all nice, but my problem is too 'soft' for mathematical laws and properties and such". Well, if you don't search, you won't find. Here's an example for a "soft" problem domain: Simon Peyton Jones, Jean-Marc Eber, Julian Seward. Composing contracts: an adventure in financial engineering. http://decenturl.com/research.microsoft/spj-financial-contracts Of course, the laws "of nature" governing your problem domain may be hard to find, so it may be worth to "just implement" and let some "law intuition" guide you. Well-known example: darcs. Regards, apfelmus
participants (10)
-
Andrew Coppin
-
apfelmus
-
Bulat Ziganshin
-
Emil Axelsson
-
Jonathan Cast
-
Jules Bean
-
Levi Stephen
-
szefirov@ot.ru
-
Thomas Schilling
-
Tim Chevalier