Tim Van Wassenhove

Passionate geek, interested in Technology. Proud father of two

30 Jan 2014

A parameterized active pattern to match the first elements of an array

Been writing code in F# for almost a year and never blogged about it. Time to change that. Earlier today someone asked the following on twitter:

Is it possible to pattern match the first part of an array in #FSharp? Something like | 1::2::3::tail but for arrays? #lazyweb

I accepted the challenge :)

let (|Array|_|) pattern toMatch =      
	let patternLength = Array.length pattern
	let toMatchLength = Array.length toMatch
	let tailLength = toMatchLength - patternLength

	if patternLength > toMatchLength then
		None
	else
		let firstElementsAreEqual = [ 0 .. (patternLength - 1) ] |> Seq.forall (fun i -> pattern.[i] = toMatch.[i])
          
		if firstElementsAreEqual then
			Some(Array.sub toMatch patternLength tailLength)
		else 
			None
		
match [|1;2;3|] with
| Array \[|1|] tail -> sprintf "bingo %i" (tail |> Array.sum) // the tail is [|2;3|\]

Or as a gist: https://gist.github.com/timvw/8717796.

23 Jan 2014

Comments are disabled on this blog

As of now comments are disabled on this blog. Want to reply? Write your own post and pingback or engage a conversation on twitter with @timvw.

27 Apr 2013

The curious case of trailing spaces in SQL

A while ago I was quite surprised to see that the following query returns 1 instead of 0:

SELECT COUNT(*) WHERE N'Tim' = N'Tim '; -- notice the trailing space

Apparently this is just standard behaviour. Here is an extract from sql1992.txt (Section 8.2 Paragraph 3):

18 Oct 2012

Sample query to demonstrate influence of collation in Sql Server

Lately I had the pleasure to investigate collations and here is a sample query that demonstrates how a collation impacts the behaviour of a query:

WITH [Words] AS (
	SELECT N'Een' AS [word]  
	UNION ALL  
	SELECT N'Eèn'	  
	UNION ALL
	SELECT N'EEN'
)
SELECT [word]	  
FROM [Words]  
WHERE [word]	  
--COLLATE Latin1_General_CS_AS -- returns Een
--COLLATE Latin1_General_CI_AI -- returns Een, Eèn and EEN
--COLLATE LAtin1_General_CI_AS -- returns Een and EEN
COLLATE Latin1_General_CS_AI -- returns Een and Eèn
= N'Een';
22 Sep 2012

Copy all mp3 files in Music folder to USB dribe

Copying all mp3 files from my Music folder to a USB drive is pretty easy on my Macbook:

find Music -name *.mp3 -exec cp {} /Volumes/SANDISK \;
06 Aug 2012

Using eID on OS X Mountain Lion

Last week or so I got myself a MacBook Air and I am really loving it so far. Today I needed to use my eID so I installed the middleware application without any hassle but had a hard time configuring Safari. Apparently Apply removed support for PKCS #11 in OS X Mountain Lion. After installing SmartCard Services the certificates appeared in the Keychain and I became able to authenticate on websites using my certificate

20 May 2012

Add missing books to iTunes

These days i read most books on my ipad. The problem is that iTunes does not seem to add pdf files when i choose ‘Add Folder’ to the library. So here is a small application that adds them one by one (way too lazy/unmotivated to do this by hand).

https://gist.github.com/2759128

26 Apr 2012

Multiclean solution

One of my favorite powershell commands when cleaning up:

$RootFolder = 'C:\tfs'
Get-ChildItem $RootFolder bin -Recurse | Remove-Item -Recurse
Get-ChildItem $RootFolder obj -Recurse | Remove-Item -Recurse
27 Mar 2012

An example of Common Table Expression and Window function usage...

Earlier this week some colleague had been assigned a maintenance task and asked me how I would solve it. Every customer is permitted to have an amount of publications. All excess publications should be removed from the system (only the n most recent ones are permitted to remain on the system).

Here is an example of the Customer table:

CREATE TABLE [dbo].[Customer](  
	[CustomerId] [int] IDENTITY(1,1) NOT NULL, 
	[CustomerName] [nvarchar](50) NOT NULL,  
	[PermittedPublications] [int] NOT NULL
);

INSERT INTO [dbo].[Customer]		  
	([CustomerName], [PermittedPublications])
VALUES
	('timvw', 2),		  
	('mike', 3);

An example of the customer publications table:

CREATE TABLE [dbo].[Publication](	  
	[PublicationId] [int] IDENTITY(1,1) NOT NULL,	  
	[CustomerId] [int] NOT NULL,	  
	[PublicationName] [nvarchar](50) NOT NULL,	  
	[PublicationTime] [datetime2] NOT NULL
);

INSERT INTO [dbo].[Publication]
  	([CustomerId], [PublicationName],[PublicationTime])
VALUES
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'timvw'), 'tim pub1', SYSUTCDATETIME()),		  
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'timvw'), 'tim pub2', SYSUTCDATETIME()),		  
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'timvw'), 'tim pub', SYSUTCDATETIME()),
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'timvw'), 'tim pub4', SYSUTCDATETIME()),
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'mike'), 'mike pub1', SYSUTCDATETIME()),
	((SELECT [CustomerId] FROM [dbo].[Customer] WHERE [CustomerName] = 'mike'), 'mike pub2', SYSUTCDATETIME()); 

My colleague was keen on using some cursor logic, but I demonstrated him how a set-based alternative:

WITH [RankedPublication] AS (	  
	SELECT [CustomerId]
		,[PublicationId]
		,[PublicationName]
		,[PublicationTime]
		,ROW_NUMBER() OVER(PARTITION BY [CustomerId] ORDER BY [PublicationTime]) AS [PublicationRank]
	FROM [dbo].[Publication]
), [ExcessPublication] AS (	  
	SELECT [PublicationId]	  
	FROM [RankedPublication]	  
	INNER JOIN [dbo].[Customer] ON [Customer].[CustomerId] = [RankedPublication].[CustomerId]	  
	WHERE [PublicationRank] > [Customer].[PermittedPublications]
)
DELETE FROM [dbo].[Publication]	  
WHERE [PublicationId] IN (SELECT [PublicationId] FROM [ExcessPublication]);
28 Oct 2011

Say no to primitives in your API.. and make your software more explicit

A while ago I wrote some code like this:

public interface ICanBroadcast
{ 
  public void Broadcast(string message) { ... }  
  public void Broadcast(string message, string author) { ... }
}

A bit later the requirements changed and from now on it was required to specify the topic:

public interface ICanBroadcast  
{
  public void Broadcast(string message, string topic) { ... }  
  public void Broadcast(string message, string author, string topic) { ... } 
}

In case you were using Broadcast(string message) the compiler would rightfully inform you that no such method exists. In case you were using Broadcast(string message, string author) the compiler does not catch the error and incorrectly uses the author as topic. I can only hope that you have a suite of tests that makes you notice that something is wrong when you upgrade to my latest release.

Let’s make the difference between an Author and a Topic more explicit (to our API consumers and the compiler) by creating explicit types to represent the concepts:

public interface ICanBroadcast 
{  
  public void Broadcast(Message message, Topic topic) { ... } 
  public void Broadcast(Message message, Author author, Topic topic) { ... }  
} 

The joy of using a typed language 😉