Hidden Features of F#
Asked Answered
F

11

23

This is the unabashed attempt of a similar C# question.

So what are your favorite F# hidden (or not) features?

Most of the features I've used so far aren't exactly hidden but have been quite refreshing. Like how trivial it is to overload operators compared to say C# or VB.NET.

And Async<T> has helped me shave off some real ugly code.

I'm quite new to the language still so it'd be great to learn what other features are being used in the wild.

Forespent answered 8/10, 2008 at 7:6 Comment(0)
W
29

User defined numeric literals can be defined by providing a module whose name starts with NumericLiteral and which defines certain methods (FromZero, FromOne, etc.).

In particular, you can use this to provide a much more readable syntax for calling LanguagePrimitives.GenericZero and LanguagePrimitives.GenericOne:

module NumericLiteralG = begin
  let inline FromZero() = LanguagePrimitives.GenericZero
  let inline FromOne() = LanguagePrimitives.GenericOne
end

let inline genericFactorial n =
  let rec fact n = if (n = 0G) then 1G else n * (fact (n - 1G))
  fact n

let flt = genericFactorial 30.
let bigI = genericFactorial 30I
Wizard answered 8/10, 2008 at 7:6 Comment(2)
Awesome idea - should be enabled by default.Soffit
This is a very nice trick, but it seems to work only with G, N, Q, R and Z.Seafood
S
10

F# has a little-used feature called "signature files". You can have a big implementation file full of public types/methods/modules/functions, but then you can hide and selectively expose that functionality to the sequel of the program via a signature file. That is, a signature file acts as a kind of screen/filter that enables you to make entities "public to this file" but "private to the rest of the program".

I feel like this is a pretty killer feature on the .Net platform, because the only other/prior tool you have for this kind of encapsulation is assemblies. If you have a small component with a few related types that want to be able to see each other's internal details, but don't want those types to have all those bits public to everyone, what can you do? Well, you can do two things:

  1. You can put that component in a separate assembly, and make the members that those types share be "internal", and make the narrow part you want everyone else to see be "public", or
  2. You just mark the internal stuff "internal" but you leave those types in your gigantic assembly and just hope that all the other code in the assembly chooses not to call those members that were only marked 'internal' because one other type needed to see it.

In my experience, on large software projects, everyone always does #2, because #1 is a non-starter for various reasons (people don't want 50 small assemblies, they want 1 or 2 or 3 large assemblies, for other maybe-good reasons unrelated to the encapsulation point I am raising (aside: everyone mentions ILMerge but no one uses it)).

So you chose option #2. Then a year later, you finally decide to refactor out that component, and you discover that over the past year, 17 other places now call into that 'internal' method that was really only meant for that one other type to call, making it really hard to factor out that bit because now everyone depends on those implementation details. Bummer.

The point is, there is no good way to create a moderate-size intra-assembly encapsulation scope/boundary in .Net. Often times "internal" is too big and "private" is too small.

... until F#. With F# signature files, you can create an encapsulation scope of "this source code file" by marking a bunch of stuff as public within the implementation file, so all the other code in the file can see it and party on it, but then use a signature file to hide all of the details expect the narrow public interface that component exposes to the rest of the world. This is happy. Define three highly related types in one file, let them see each others implementation details, but only expose the truly public stuff to everyone else. Win!

Signature files are perhaps not the ideal feature for intra-assembly encapsulation boundaries, but they are the only such feature I know, and so I cling to them like a life raft in the ocean.

TL;DR

Complexity is the enemy. Encapsulation boundaries are a weapon against this enemy. "private" is a great weapon but sometimes too small to be applicable, and "internal" is often too weak because so much code (entire assembly and all InternalsVisibleTo's) can see internal stuff. F# offers a scope bigger than "private to a type" but smaller than "the whole assembly", and that is very useful.

Strung answered 8/10, 2008 at 7:6 Comment(0)
S
9

I wonder what happens if you add

<appSettings>
  <add key="fsharp-navigationbar-enabled" value="true" />
</appSettings>

to your devenv.exe.config file? (Use at your own risk.)

Strung answered 8/10, 2008 at 7:6 Comment(3)
Any chance you'll let us know without us actually trying it?Scott
Is there any reason I shouldn't leave this on? It's nice to have. Do you know if it will be in vNext?Sirdar
It is not well-tested, possibly may make VS more prone to crashing. Don't know yet if it will be in vNext... there's also maybe "Solution Navigator", and gotta see what we can fit into the schedule...Strung
S
8

Passing --warnon:1182 to the compiler turns on warnings about unused variables; variable names that begin with underscore are immune.

Strung answered 8/10, 2008 at 7:6 Comment(2)
Thank-you! This was not on the Compiler Options page! Truly hidden.Buy
Interesting, as of F# 2.0, the compiler issues this warning: FSC : warning FS0075: The command-line option '--warnon' is for internal use onlyBuy
S
7

Automatically-generated comparison functions for algebraic data types (based on lexicographical ordering) is a nice feature that is relatively unknown; see

http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!548.entry

for an example.

Strung answered 17/10, 2008 at 10:46 Comment(0)
S
6

See this question

F# operator "?"

for info on the question-mark operator and how it provides the basic language mechanism to build a feature akin to 'dynamic' in C#.

Strung answered 8/10, 2008 at 7:6 Comment(0)
C
6

Yes, F# doesn't have any 'hidden' features, but it sure does have a lot of power packed into the simple language. A less-known feature of the language, is where you can basically enable duck typing despite the fact F# is staticaly typed.

Cuneiform answered 9/10, 2008 at 5:19 Comment(0)
L
5

Not really hidden, but as a non-ML person this escaped me for quite a while:

Pattern matching can decompose arbitrarily deep into data structures.

Here's a [incredibly arbitrary] nested tuple example; this works on lists or unions or any combinations of nested values:

let listEven =
  "Manipulating strings can be intriguing using F#".Split ' '
  |> List.ofArray
  |> List.map (fun x -> (x.Length % 2 = 0, x.Contains "i"), x)
  |> List.choose 
     ( function (true, true), s -> Some s 
              | _, "F#"         -> Some "language" 
              | _               -> None ) 
Limpkin answered 8/10, 2008 at 7:6 Comment(0)
H
3

Use of F# as a utility scripting language may be under appreciated. F# enthusiasts tend to be quants. Sometimes you want something to back up your MP3s (or dozens of database servers) that's a little more robust than batch. I've been hunting for a modern replacement for jscript / vbscript. Lately, I've used IronPython, but F# may be more complete and the .NET interaction is less cumbersome.

I like curried functions for entertainment value. Show a curried function to a pure procedural / OOP program for at least three WTFs. Starting with this is a bad way to get F# converts, though :)

Headword answered 8/10, 2008 at 7:6 Comment(2)
I like using FSI for little scripts, and I've actually used some .fsx files for things. However, at least in my experience, the startup time required for .fsx files is too much for general purpose scripting. I still prefer batch files and Perl scripts for scripting.Fivespot
Try PowerShell and you won't regret :)Antonomasia
W
2

Inlined operators on generic types can have different generic constraints:

type 'a Wrapper = Wrapper of 'a with
  static member inline (+)(Wrapper(a),Wrapper(b)) = Wrapper(a + b)
  static member inline Exp(Wrapper(a)) = Wrapper(exp a)

let objWrapper = Wrapper(obj())
let intWrapper = (Wrapper 1) + (Wrapper 2)
let fltWrapper = exp (Wrapper 1.0)

(* won''t compile *)
let _ = exp (Wrapper 1)
Wizard answered 8/10, 2008 at 7:6 Comment(2)
Just to clarify, what would happen if (+) and Exp were not declared as inline?Zeb
@Joh: The operators would no longer be polymorphic, so for instance you could only use the (+) operator on a single type of Wrapper within your program. All of the code I've written above would still work, but with the inlined operators, we could also add two float Wrappers as well.Wizard
C
1

There are no hidden features, because F# is in design mode. All what we have is a Technical Preview, which changes every two month.

see http://research.microsoft.com/fsharp/

Crus answered 8/10, 2008 at 7:14 Comment(1)
I very much guess therefore every feature is somehow hidden ;)Joinder

© 2022 - 2024 — McMap. All rights reserved.