Since we've made something, let's make it cooler
Lesson 2: Now That We're Programmers, Let's Make Something
Lesson 4: Moving on to Solving Business Problems
Now that we've built something that actually resembles a business problem, and I want to take this time to show you one of the coolest features of F# in my opinion. So in this post, I want to introduce the conecpt of a "DSL", or a "domain-specific language".
F# has a very powerful feature that allows you to create your own operators. What do I mean by operators? Things like
-, or the
|> operator. You can build your own, which leads to building your own DSL.
What, exactly, is a DSL?
I mentioned the name "domain-specific language", but I know without context that has little meaning, so let's clarify. By building our own DSL we're actually creating a language that is a subset of another language. We're creating our own language! We don't even have to do much to make it happen, so let's investigate that a little bit.
First off, a DSL isn't always apporpriate, you need to take care to understand what a DSL applies to, and when to use it. As a language, F# already has a lot of operators, including some confusing ones, so adding more to the mix is probably not the greatest idea unless it makes sense, and it's going to be used a lot.
In work I've actually done this, and I'm going to show off the operator I created, how it works, and how you can create one. It's really quite simple, and if you've tried experimenting with the language you probably already learned that it's possible, and may have created one yourself. If so, congrats! That's awesome! You clearly figured it out faster than me, and I applaud you for that, because I didn't figure this out until a very long time after I started using F#.
Let's make a DSL
Seriously, let's create one. Remember that
split function we built before? It was pretty basic, wasn't it? We took a
char and a
string, and we split apporpriately, but it's about time for us to make it a little easier.
In the work I do we do a lot of string parsing, which means a lot of calling
split. In fact, I do it so often that I have a
String.fs library file, that has this (and a few other) nifty functions in it. I carry that library to all my F# projects, and it saves me a great deal of time.
let split (c:char) (s:string) = s.Split(c)
Remember this method? Pretty basic, take a string and split on the char. We can partially apply it, even, to make it simpler if we're splitting on the same string a lot. Now what if I told you we could build our own operator to make this work? What if we could create something like:
let splitString = someString <|> someChar
<|> operator? Try to use it in F# right now, you probably can't. (Unless you already created it, then feel free to use it.) Because I'm a nice person I'm going to just give you this operator for free.
let (<|>) (s:string) (c:char) = s |> split c
It is that complex, adding your own operator is as easy as defining a function with the operator in parenthesis as the function name. We could literally create anything, the issue is, should we?
Be careful when defining a DSL
DSL's can be powerful things - they can make complex things simple, and they can make simple things complex. You want to define a DSL that fits, and a DSL that is powerful. Don't define a DSL "just because you can" - I've done so, bad idea.
However, if you define a DSL well. you can turn something increasingly complex into a much simpler feeling, it feels like a built-in operator. It feels like it belongs. It just feels natural.
Since this post is coming out on a Friday (at least here in the Eastern Time Zone - could easily be Saturday for many of you) you probably don't have to work tomorrow (or today, if it's already Saturday), so if you get a chance, experiment with custom operators. Try to figure out how they work, this one little feature can truly make things easy on you. Shoot, you can even try to build the opposite operator:
>|< if you like, it should fit in with the code below. (Though in my real DSL for this, the operator is actually
><, but I demonstrated it as
>|< since that's more directly transpated as the opposite of what we have.)
let names =
rawNames <|> ' '
|> List.fold (fun (acc:string list) str ->
match acc with
| prev::tail when prev.Length <= 3 -> (prev >|< " " >|< str)::tail
| _ -> str::acc) 
Bonus day: documentation
We've gone through writing some code, but the biggest thing people get caught up on is writing documentation. Now, on one hand, I'm a firm believer that good code documents itself. On the other hand, I know that I very infrequently write good code. So we always need a way to tell the "consumer" (often times ourselves, but if you're working on some API think of the people who will be using it) what to do with our method/function/variable/thing. This comes in many forms, one of which is the Visual Studio / F# documentation comments.
Have you ever wondered how people can put text into the intellisense-hover-dealy of the function/variable/parameter they provide? It's actually quite easy, and in F# it's trivial: use a triple-slash comment:
/// Splits a string into an array on the specified char.
let (<|>) (s:string) (c:char) = s |> split c
Now when we hover any instance of our
<|> operator, we should see that string show up:
Damn that's cool. If you're building an F# project, you can also generate an XML file with all the triple-slash comment docs in it. Simply open the project properties, go to "Build" and check the "XML Documentation File" box. Visual Studio will then generate a
.xml file in the same directory as your
.dll that contains these comments in a form that Visual Studio can understand, and that can be used to generate web documentation. (This is part of how MSDN is built - the code comment documentation becomes the web documentation.)
As an aside, I want to tell you all how grateful I am to have met some of the developer comrades I have - a lot of them have been extremely helpful to me regarding programming, software development in general, and even doing things right.I was actually thinking about each one of them as I wrote this, and I would call them out by name but I don't want to reveal any personal information, so for those of you that I'm referring to (and you all know who you are): thank you.