Let ``it correctly converts casing to PascalCase`` value expected =įor our Haskell tests we'll be using Hspec. Let ``it correctly converts casing to camelCase`` value expected = In this case we're able to leverage the Theory and InlineData attributes to easily add new test cases without needing to duplicate a bunch of code. One thing that I really like about xUnit is their support for data-driven tests. What kind of developers would we be if we didn't add some unit tests to ensure that everything is working as expected?įor F# we'll be using FsUnit and xUnit to write our tests. It is possible to implement the F# version in point-free style, or the Haskell version using a pipeline operator, but I wanted to keep the implementations as idiomatic as possible for their respective languages. The one major difference between the F# and Haskell implementations is that the Haskell casing functions are implemented completely in point-free style. ToScreamingSnakeCase = intercalate " _ ". Here's what our public API looks like in F#: let toCamelCase value =Īnd here's the public API in Haskell: toCamelCase :: String -> String The beauty of this approach is that if we ever wanted to add a new case it would be super simple to do so. Now that the hard part is done we can finally move onto the actual API of the library itself! This is probably the simplest part of the library, as we just have to define what the casing rules are for each of our desired cases. I personally find the Haskell version a bit cleaner, especially with Haskell's support for pattern matching in the function definition. In getWords' currentWord' acc' remainingChars' Then (appendCurrentChar "", currentWord : acc)Įlse (appendCurrentChar currentWord, acc) Then ( "", appendCurrentChar currentWord : acc)Įlse if all Char. GetWords' currentWord acc (currentChar : nextChar : remainingChars) = GetWords' currentWord acc (singleChar : ) = GetWords' currentWord acc = currentWord : acc GetWords value = reverse $ catMaybes $ map stringToMaybe $ getWords' "" value Our getWords implementation looks roughly the same in Haskell: isSeparator :: Char -> BoolĬhar. GetWords' currentWord words remainingChars If not ^ isSeparator nextChar then nextChar :: remainingChars "" |> appendCurrentChar, currentWord :: words "", appendCurrentChar currentWord :: wordsĮlif currentWord |> Seq.forall Char.IsUpper & Char.IsUpper currentChar & Char.IsLower nextChar then | currentChar :: nextChar :: remainingChars -> Let rec getWords ' currentWord words chars = Let private getWords ( value : string ) = IsSeparator nextChar || isCasingBoundary () Let isCasingBoundary () = Char.IsLower currentChar & Char.IsUpper nextChar Let private isBoundary currentChar nextChar = Our core algorithm is implemented in getWords, which we use to break a string up into a series of words. With all of the setup done we can now move on to implementing the core library logic. MapTail mapping (x : xs) = x : map mapping xsĬapitalize = mapHead Char. MapHead mapping (x : xs) = mapping x : xs We're going to need some helper functions in Haskell as well: stringToMaybe :: String -> Maybe String Let private capitalize ( value : string ) = Match value |> String.IsNullOrWhiteSpace with Side note, if you're wondering about what (^) is, it's an operator that performs function application, much like ($) in Haskell. Not all of these helper functions are strictly needed for the F# version, but I think that they make things a little more ergonomic. Now that we have our module setup, it's time to declare some helper functions. ![]() Here's what the top of our Haskell module looks like: ![]() ![]() In F# we can use visibility keywords to govern which functions are exposed outside of the module. We have slightly more work to do in the Haskell version since we're going to explicitly state which functions we want to export. In F# this is just a few lines: module Casing The first thing we'll need to do is create a module for the library. The library is inspired by heck, a case conversion library for Rust.Īfter completing the initial implementation I thought it would be a fun exercise to implement the library in Haskell. At work I recently implemented a small library in F# for performing case conversion.
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |