Learning haskell through project euler
I have tried using Haskell to various smaller projects, such as wishsys and a game that I never got really far into making. But learning a new programming language through the means of hobby projects only work as long as the project is contained and small. For my part, most hobby projects start out with great ideas and grand designs, but end up as a mess since I am unfamiliar with the programming language.
When using a new programming language, time is spent learning the language rather than developing the project. This in turn means that I end up learning the bare minimum to get the job done. And this defeats the purpose of using a project to learn a new language. If the goal is to finish the project, you should have used something you know well and feel most productive with. If the goals is to learn a programming language, you should start out with a small project instead.
For me, project euler is a great way to learn Haskell, because it contains a lot of problems that Haskell (and functional languages in general) is the perfect tool for solving. The projects I mentioned above involves using databases, multiple threads and other scary real world stuff, but I just wanted to learn Haskell. And better yet, once you have solved a problem, chances are you can find someone with an even more elegant solution written in the same programming language you are using. A great way to learn!
Dinner menu week 18
This time I thought I’d share our dinner plans for the next week. We take turns creating dinner list every week, and next week is my turn!
- Monday: Tortellini with sun dried tomatoes and mozzarella
- Tuesday: Fish with avocado, ruccola salad and hot mustard
- Wednesday: Fennel soup with chicken
- Thursday: Albondigas
- Friday: Salmon with fennelrisotto
- Saturday: Breaded cod with salad and potatoes
- Sunday: Home made tomato soup
Lets hope it tastes as good as I think it does.
Haskell is awesome
I have started to learn myself haskell using the book named “Real world Haskell”. I have so far only come to chapter 4, but I am already in love with some of the features:
-
Its strict static type system, which makes it easy to understand what a function does. Moreover, it allows you to think through what your code is going to do as well as make the decisions of what to do for special cases up front. The following is a definition of a function which compares the length of two lists, and returns their order (==, <, >). The definition clearly states that it operates on two lists of any type, and returns a value of type Ordering. Crystal clear!
listCmp :: [a] -> [a] -> Ordering
-
Partially due to the above point, one can avoid unpleasant bugs later on, because you chose to postpone your decision on what to do with your input.
-
Pattern matching. I came across this in the Oz programming language when I was in university, but I didn’t really understand how powerful and readable everything becomes until using it in Haskell. The following function takes a separator and a list of lists as argument, and combines the lists using the separator:
intersperse :: a -> [ [a] ] -> [a] intersperse sep [] = [] intersperse sep (x:[]) = x intersperse sep (x:xs) = x ++ [sep] ++ (intersperse sep xs)
I love how you can just look at the patterns to see what cases is covered by the function, rather than nesting into some complex if sentence.
-
Readability when using ‘where’ syntax. This is the implementation of the listCmp function:
listCmp lhs rhs | lengthLhs < lengthRhs = LT | lengthLhs > lengthRhs = GT | otherwise = EQ where lengthLhs = (length lhs) lengthRhs = (length rhs)
What I like about it is that you can separate the logic performed on values from the function calls, so that when you read the code, you see the actual computation done by the function in the different cases. You can also do this with the let syntax, but I think the above reads really well.
Back to compiling software
For a while now, I have been using Ubuntu Linux on my desktop, and it as worked really well. In fact, I even installed Windows 7 on my media center (replacing Linux) just to stop bothering with configuring my system all the time. Since I started working at Yahoo!, I did not really feel like having to do extra work at home in order for my computer to function properly. Moreover, I did not have much time left to work on FreeBSD, so I simply reinstalled my desktop with Linux, and that has been working well for almost a year now.
But recently I have sort of missed working on FreeBSD, so I decided to give it at try again from a user perspective. Many of the things I feel was lacking is still there. However, the things that were good, are still good. So far, I have been able to install all software that I wanted to install, but I still feel that we need something better on top of ports in order to make it easier for users. Hopefully, some of the initiatives that I have seen on the mailing list will not die any time soon. Apart from ports, many of the common tasks are pretty manual too. Configuring the system should be more straightforward than having to guess and edit what should be in /etc/rc.conf. Though many of the issues I encounter comes from the fact that FreeBSD has a very small userbase, and is simply not prioritized by many companies, there are a lot of things that can be improved irregardless of that. If i start doing any more FreeBSD work, it is most likely to be in the “make-it-less-painful-to-use”-department.
Using 4k sector drives
I just bought two Western Digital 2 TB disks the other day in order to increase storage capacity. I was planning on putting a ZFS mirror on them. The other day I discovered that the disks uses a new drive format called “Advanced Disk Format”. This format basically extends the sector size from 512 to 4096 bytes.
The problem is that the disks report their sector size to be 512 rather than 4096 in order for them to work well with existing operating systems. The issues with these disks are discussed here and here.
To summarize, this results in two main problems:
-
Partitioning tools operate on 512 bytes “logical” sectors, which may result in a partition starting at a non-aligned (compared to 4096 bytes) physical sector. If using partitioning tools that are not updated to align partitions to 4k, a request may cause a write to more than one sector.
-
File systems/disk consumers think the underlying device has a 512 byte sector size, and issues requests that are below 4096 bytes. For a write request, this is catastrophic, because in order to write only parts of a block, the disk will have to read the block and modify the part that changed, before writing it back to disk (Read-modify-write).
Dag Erling Smørgrav made a tool to benchmark disk performance using aligned and misaligned writes (mentioned in his post above (svn co svn://svn.freebsd.org/base/user/des/phybs). Here are the results:
nobby# ./phybs -w /dev/gpt/storage0
count size offset step msec tps kBps
131072 1024 0 4096 131771 16 994
131072 1024 512 4096 136005 16 963
65536 2048 0 8192 74762 14 1753
65536 2048 512 8192 71407 15 1835
65536 2048 1024 8192 73432 15 1784
32768 4096 0 16384 20710 130 6328
32768 4096 512 16384 61987 43 2114
32768 4096 1024 16384 62719 43 2089
32768 4096 2048 16384 61089 44 2145
16384 8192 0 32768 14238 245 9205
16384 8192 512 32768 53348 65 2456
16384 8192 1024 32768 52868 66 2479
16384 8192 2048 32768 50914 68 2574
Clearly, using < 4k blocks results in bad performance. Using blocks larger than 4k results in a 3x speedup.
The way I solved this in FreeBSD was to partition the disk manually with gpart and set the partition start to a multiple of 8 (8 * 512 = 4096). All partitions on the disk should start at a sector number that is a multiple of 8.
ZFS uses variable block sizes for its requests, which can pose a problem when the underlying provider reports a sector size of 512 bytes. In order to override this, I used gnop(8), which can create a provider on top of another provider with different characteristics: gnop create -o 4096 -S 4096
The -o parameter makes sure that the new provider does not conflict with the original provider when ZFS tries to detect any filesystems on the disk. The second parameter sets the sector size of the new parameter to 4096, which makes sure that all requests going to the disk from ZFS will be in 4k blocks.
For UFS, the default fixed block size is 16k, so there should be no worries about it using lower block sizes. Moreover, newfs provides a -S parameter, which overrides the sector size of the underlying provider. I have not tried using UFS on these disks, but I don’t see any reason for it not working.