I'm having a dip into F# and am attempting not to write it like it's C#.
One area that bothers me is using members of System.String
. These often need to be chained together, but the only way to do so that I've seen so far is pretty much the same as C#.
For example, I've written a function to generate the pretty bit at the end of a URL (similar to SE):
module String =
open System.Text.RegularExpressions
///Makes a string suitable for use as part of a URL:
///Removes syntax, replaces spaces with dashes and lowers case,
///then trims first word after 30 chars
let prettify (x:string) =
let parsed = Regex.Replace(x.Trim(),"[^0-9a-zA-Z ]","").Replace(" ","-").ToLower()
match String.length parsed with
| m when m > 30 ->
match parsed.IndexOf("-",30-1) with
| p when p < 0 -> parsed
| p -> parsed.[..p]
| _ -> parsed
Is there a way to refactor this to be "better F#"? I'm particularly concerned about how to better handle the methods on System.String
and Regex.Replace
. (The match part is a little convoluted to handle the edge case of the last word being the one that takes it over 30 chars.)
-
\$\begingroup\$ I posted my latest iteration of this method. There have been a few, but all had the horrible String functions line. \$\endgroup\$Jon Egerton– Jon Egerton2014年07月18日 14:09:14 +00:00Commented Jul 18, 2014 at 14:09
-
\$\begingroup\$ IMHO before focusing on syntax/style you should focus on conceptual properties. For example, that function is partial and impure. Most likely you don't want that in such a function. \$\endgroup\$Mauricio Scheffer– Mauricio Scheffer2014年07月18日 19:47:41 +00:00Commented Jul 18, 2014 at 19:47
-
\$\begingroup\$ @MauricioScheffer: Thanks for feeding back to me. Could you give me a quick pointer on both of those aspects please? \$\endgroup\$Jon Egerton– Jon Egerton2014年07月19日 17:07:44 +00:00Commented Jul 19, 2014 at 17:07
-
\$\begingroup\$ It's partial because it doesn't terminate for certain inputs (e.g. an exception). It's impure because its output depends on a variable that is not part of its input (see programmers.stackexchange.com/questions/184574/… ). As an exercise, try to figure out which inputs cause it to throw, and what is the "implicit" input variable that makes it impure. \$\endgroup\$Mauricio Scheffer– Mauricio Scheffer2014年07月19日 17:42:07 +00:00Commented Jul 19, 2014 at 17:42
1 Answer 1
F# does have a String
module, but it's almost empty. If you want to make string manipulations look more idiomatic, you'll have write functions that wrap System.String
methods yourself (quick search didn't find anything existing):
let trim (input:string) =
input.Trim()
let regexReplace (pattern:string) (replacement:string) (input:string) =
Regex.Replace(input, pattern, replacement)
let replace (pattern:string) (replacement:string) (input:string) =
input.Replace(pattern, replacement)
let toLower (input:string) =
input.ToLower()
...
let parsed =
x
|> trim
|> regexReplace "[^0-9a-zA-Z ]" ""
|> replace " " "-"
|> toLower
match String.length parsed with
| m when m > 30 -> ...
| _ -> parsed
You don't need to try to differentiate from C# so hard. if
would be more appropriate here:
if String.length parsed > 30 then ...
else parsed
match parsed.IndexOf("-",30-1) with
| p when p < 0 -> parsed
| p -> parsed.[..p]
You can rewrite this to avoid having to reference p
in both branches:
match parsed.IndexOf("-",30-1) with
| p when p >= 0 -> parsed.[..p]
| _ -> parsed
Also, use more descriptive names than p
, like index
or maybe pos
.
Also, this code usually returns a string that's over 30 characters and ends with -
. Is that what you intended?
-
\$\begingroup\$ Thanks for that. I'd found the string module and used
length
from it, but as you say, the other functions in it weren't very helpful here. I'd considered those wrappers, as an option if I need to do a lot of such manipulation. \$\endgroup\$Jon Egerton– Jon Egerton2014年07月21日 14:18:14 +00:00Commented Jul 21, 2014 at 14:18 -
\$\begingroup\$ btw, its intended to be >30 often as I want the last bit to be a full word. Will adjust for the trailing - \$\endgroup\$Jon Egerton– Jon Egerton2014年07月21日 14:19:15 +00:00Commented Jul 21, 2014 at 14:19