2014-09-29 16 views
6

Ich habe oft eine Funktion mit mehreren Parametern des gleichen Typs, und manchmal verwenden sie in der falschen Reihenfolge. Als einfaches BeispielF # Phantom-Typen in der Praxis

let combinePath (path : string) (fileName : string) = ... 

Es scheint mir, dass die Phantomtypen ein guter Weg sein würde, keine Verwechslungen zu fangen. Aber ich verstehe nicht, wie man das Beispiel in der einzigen F# phantom types question anwendet.

Wie würde ich Phantomtypen in diesem Beispiel implementieren? Wie würde ich combatePath aufrufen? Oder fehlt mir eine einfachere Lösung des Problems?

Antwort

12

Ich denke, der einfachste Weg, diskriminierte Gewerkschaften zu verwenden ist:

type Path = Path of string 
type Fname = Fname of string 
let combinePath (Path(p)) (Fname(file)) = System.IO.Path.Combine(p, file) 

Sie es

so nennen konnte
combinePath (Path(@"C:\temp")) (Fname("temp.txt")) 
+5

Da das ist ein Fall DUs, können Sie auch combinePath (Path (p)) (Fname (f)) = ... '' do lassen, das Ende -Ergebnis wird das gleiche sein :) –

+0

Ja. Es wird einfacher sein. Aktualisierter Beitrag, danke! – Petr

+0

Das würde bedeuten, einen Typ für jeden Parametertyp zu deklarieren. Ich denke, ich dachte, es könnte eine Art von Maßeinheiten, String String sein. Obwohl es auf diese Weise immer noch Typen für Pfad und Dateiname benötigt, ist es am Ende doch egal ... ich antworte auf meinen eigenen Punkt. –

7

ich mit Petr Das Nehmen zustimmen, aber der Vollständigkeit halber, beachten Sie, dass Sie nur Verwenden Sie Phantomtypen, wenn Sie einen generischen Typ haben, um sie zu verwenden, so dass Sie nichts mit einfachen string Eingaben machen können. Stattdessen könnten Sie so etwas tun:

type safeString<'a> = { value : string } 

type Path = class end 
type FileName = class end 

let combinePath (path:safeString<Path>) (filename:safeString<FileName>) = ... 

let myPath : safeString<Path> = { value = "C:\\SomeDir\\" } 
let myFile : safeString<FileName> = { value = "MyDocument.txt" } 

// works 
let combined = combinePath myPath myFile 

// compile-time failure 
let combined' = combinePath myFile myPath 
+0

Offensichtlich ist die diskriminierte Verbindung der bessere Weg, dies zu tun. Phantomtypen scheinen für F # überflüssig zu sein. Ich denke, deshalb gibt es nicht viel davon. –

+0

@FsharpPete - Ich denke nicht, dass das ganz richtig ist; In diesem speziellen Szenario sind DUs möglicherweise besser, aber es gibt sicherlich Szenarien, in denen Phantomtypen nützlich sind (und ich denke nicht, dass F # sich merklich von den meisten anderen Sprachen unterscheidet, wenn es darum geht, die eine oder die andere Technik zu verwenden). – kvb

+0

Ja, ich sehe es jetzt. Mit dem Phantom-Typ-Ansatz können Sie den Phantom-Typ von außerhalb des Moduls/Pakets nicht so mit der diskriminierten Vereinigung angeben. –