My F# Journey – What I’ve learned so far

fsharpThis is a first blog post in a series to document my experiences while learning functional programming. This is my F# Journey.

During my 15 year career that started in the late 90’s, I have not made very many conscious decisions about learning a specific language or technology.  The path I’ve taken and the experience I’ve gained seems like it just happened naturally.

As someone always wanting to learn, I usually find some interesting topics and start going down the rabbit hole (Domain Driven Design, CQRS, Event Sourcing… thanks Greg Young), however I never usually set out on a “I’m going to learn X” journey.

That’s about to change.

As with anyone that keeps up with the latest trends, functional programming is all the rage.  And I do believe for good reason (more on that later),  which is why I’ve decided to take on the journey of learning with F#.   I’ve chosen F# because of my .NET/C# background and feel it can help my career to keep it in the .NET ecosystem.

What I’ve learned so far…

Stop comparing it to C#

apple-vs-orange

It’s natural when looking at another imperative language that you are unfamiliar with, to compare it to the language you know best.   The concepts are all transferable.  How do I perform a loop?  How do I define a variable?  How do I assign a variable?

With a quick search and reading a few examples,  you are off to the races writing some basic code in a new imperative language.  Stop trying to compare concepts.

Let it go!

Open up your mind to new ideas and try and forget everything you know.

Think like a beginner.

Just because you can, doesn’t mean you should

Because F# is a hybrid language and supports some of the concepts of an imperative language, doesn’t mean you should use them.  In pure functional languages, there are no loops or objects.

Let it go!

Just because you can use F# in a non-functional way, doesn’t mean you should (especially while learning).

When reading intro articles, you will see the following statements over and over again: “start thinking functionally” or “start thinking differently”.  It’s hard at first to really grasp what this really means.  Once you finally let go of the imperative way of thinking, you will get an “AH HA!” moment.

Read & Play

Anytime I’ve ever learned a new language it has always been through practical use in a small app.  However, I do find that learning the basics of F#, understanding F# types, and thinking like a beginner before jumping into real code has been helpful.  I’m using Visual Studio and writing code, but more as a playground than attempting actually write an app.  Once I feel comfortable enough and actually feel like I fully “get it”, I’m going to start writing a simple app.

Query Objects with a Mediator

Mediator

In my previous blog Query Objects instead of Repositories, I demonstrated creating query objects and handlers to encapsulate and execute query logic instead of polluting a repository with both read and write methods.  Since we have moved away from repositories and are now using query objects, we will introduce the Mediator pattern. It will allows have a common interface that can be injected into our controller or various parts of our application. The mediator will delegate our query objects to the appropriate handler that will perform the query and return the results.

First we will create an interface that will be used on all of our query objects.

public interface IQuery<out TResponse> { }

Now we need to create an interface that all of our query handlers will implement.

public interface IHandleQueries<in TQuery, out TResponse>
	where TQuery : IQuery<TResponse>
{
	TResponse Handle(TQuery query);
}

Next we will create our Mediator interface. Most examples you will see that are implementing command handlers generally are showing an IFakeBus or something similar. The difference being that generally in the Bus implementation there is no return type. On the query side, our intent is to return data.

public interface IMediate
{
	TResponse Request<TResponse>(IQuery<TResponse> query);
}

There are many ways you can implement your mediator. As an example:

public class Mediator : IMediate
{
	public delegate object Creator(Mediator container);

	private readonly Dictionary<Type, Creator> _typeToCreator = new Dictionary<Type, Creator>();

	public void Register<T>(Creator creator)
	{
		_typeToCreator.Add(typeof(T), creator);
	}

	private T Create<T>()
	{
		return (T)_typeToCreator[typeof(T)](this);
	}

	public TResponse Request<TResponse>(IQuery<TResponse> query)
	{
		var handler = Create<IHandleQueries<IQuery<TResponse>, TResponse>>();
		return handler.Handle(query);
	}
 }

Now that we have our interfaces and mediator implementation, we need to modify our existing queries and handlers.

public class ProductDetailsQuery : IQuery<ProductDetailModel>
{
	public Guid ProductId { get; private set; }

	public ProductDetailsQuery(Guid productId)
	{
		ProductId = productId;
	}
}

public class ProductDetailQueryHandler : IHandleQueries<ProductDetailsQuery, ProductDetailModel>
{
	private DbContext _db;
 
	public ProductDetailQueryHandler(DbContext db)
	{
		_db = db;
	}
 
	public ProductDetailModel Handle(ProductDetailsQuery query)
	{
		var product = (from p in _db.Products where p.ProductId == query.ProductId).SingleOrDefault();
		if (product == null) {
			throw new InvalidOperationException("Product does not exist.");
		}
 
 		var relatedProducts = (from p in _db.RecommendedProducts where p.PrimaryProductId == query.ProductId);
 
 		return new ProductDetailsModel
		{
			Id = product.Id,
			Name = product.Name,
			Price = product.Price,
			PriceFormatted = product.Price.ToString("C"),
			RecommendedProducts = (from x in relatedProducts select new ProductDetailModel.RecommendedProducts {
				ProductId = x.RecommendedProductId,
				Name = x.Name,
				Price = x.Price,
				PriceFormatted = x.Price.ToString("C")
			})
		};
	}
 }

Now in our controller, instead of either creating a new instance of the query handler in our controllers or having all them injected into the constructor, we now simply inject the mediator.

public class ProductController : Controller
{
	private IMediate _mediator;
	
	public ProductController(IMediate mediator)
	{
		_mediator = mediator;
	}
	
	public ViewResult ProductDetails(ProductDetailQuery query)
	{
		var model = _mediator.Request(query);
		return View(model);
	}
}

As before, we have encapsulated the generation of our view model into its own object but now a common interface in a mediator to handle the incoming query object requests.

Query Objects instead of Repositories

QueryThe repository pattern is often used to encapsulate simple and sometimes rather complex query logic.   However, it has also been morphed into handling persistence and is often used as another layer of abstraction from your data mapping layer.   This blog post show you how to slim down and simplify your repositories or possibly eliminate them all together by using query objects.

A typical repository will look something like this:

public interface IProductRepository
{
	void Insert(Product product);
	void Delete(Product product);
	IEnumerable<Product> GetById(Guid id);
	IEnumerable<Product> GetAllActive();
	IEnumerable<Product> FindByName(string name);
	IEnumerable<Product> FindBySku(string name);
	IEnumerable<Product> Find(string keyword, int limit, int page);
	IEnumerable<Product> GetRelated(Guid id);
}

Each of the Get/Find methods implemented above would encapsulate a specific a query.  This type of repository would most likely be used to then transform the data returned from a set of methods into a View Model which would then be passed to our view or serialized and sent back to the caller (browser).  As an example of passing the model to a view in ASP.NET MVC, it would look something like this:

public ViewResult ProductDetails(Guid productId)
{
	var product = _productRepository.GetById(productId);
	var relatedProducts = _productRepository.GetRelated(productId);
	
	var model = new ProductDetailsModel
	{
		Id = product.Id,
		Name = product.Name,
		Price = product.Price,
		PriceFormatted = product.Price.ToString("C"),
		RecommendedProducts = (from x in relatedProducts select new ProductDetailModel.RecommendedProducts {
				ProductId = x.RecommendedProductId,
				Name = x.Name,
				Price = x.Price,
				PriceFormatted = x.Price.ToString("C")
			})
	};

	return View(model);
}

As I’ve mentioned before, I believe you should think of your MVC framework as an HTTP interface to your application.  Regardless if you are returning HTML or JSON, the generation of your View Model should not be coupled to your MVC framework.

Query Objects

In the example above, I want to extract the generation of my view model info a query object.  A query object is similar to a command object for processing behavior in our domain.  Our query object will now look like this:

public class ProductDetailsQuery
{
	public Guid ProductID { get; private set; }
	
	public ProductDetailQuery(Guid productId)
	{
		ProductId = productId
	}
}

In order to execute our query, we will implement in a query handler.

public class ProductDetailQueryHandler
{
	private DbContext _db;
	
	public ProductDetailQueryHandler(DbContext db)
	{
		_db = db;
	}
	
	public ProductDetailModel Handle(ProductDetailQuery query)
	{
		var product = (from p in _db.Products where p.ProductId == query.ProductId).SingleOrDefault();
		if (product == null) {
			throw new InvalidOperationException("Product does not exist.");
		}
		
		var relatedProducts = (from p in _db.RecommendedProducts where p.PrimaryProductId == query.ProductId);
		
		return new ProductDetailsModel
		{
			Id = product.Id,
			Name = product.Name,
			Price = product.Price,
			PriceFormatted = product.Price.ToString("C"),
			RecommendedProducts = (from x in relatedProducts select new ProductDetailModel.RecommendedProducts {
				ProductId = x.RecommendedProductId,
				Name = x.Name,
				Price = x.Price,
				PriceFormatted = x.Price.ToString("C")
			})
		};
	}
}

Now we have encapsulated the generation of our view model which can be used in our controller.

public ViewResult ProductDetails(ProductDetailQuery query)
{
	var model = _queryHandler(query);
	return View(model);
}

Now our controller is responsible for delegating the call to generate the model and action result or serialization.  In my next post I will take this a step further by introducing a common interface for our query handler in order to accept multiple query objects and  return types.