2014-02-18 4 views
12

Ich möchte, dies zu tun in der Lage:F # Operator für die Konvertierung von mehreren verschiedenen Maßeinheiten Überlastung

let duration = 1<hours> + 2<minutes> + 3<seconds> 

mit den folgenden Typen und Funktionen (und möglicherweise mehr Maßeinheiten):

type [<Measure>] seconds  
type [<Measure>] minutes 
type [<Measure>] hours 

let seconds_per_minute = 60<seconds>/1<minutes> 
let minutes_per_hour = 60<minutes>/1<hours> 

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds 
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes 

Also im Grunde "Stunden_zu_Minuten" sollte zum Hinzufügen von Stunden und Minuten verwendet werden, und "Minuten_zu_Sekunden" sollte verwendet werden, um Minuten und Sekunden hinzuzufügen, wenn ich es wie oben eingeben.

Ist dies in F # möglich?

Antwort

15

Eigentlich ist es möglich, es gibt einen Weg, dies zu tun:

type [<Measure>] seconds  
type [<Measure>] minutes 
type [<Measure>] hours 

let seconds_per_minute = 60<seconds>/1<minutes> 
let minutes_per_hour = 60<minutes>/1<hours> 

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds 
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes 

type D1 = D1 
type D2 = D2 

type Sum = Sum with 
    static member inline ($) (Sum, _:^t when ^t: null and ^t: struct) = id 
    static member inline ($) (Sum, b)    = fun _ _ a -> a + b 
    static member  ($) (Sum, b:int<minutes>) = fun D1 _ a -> hours_to_minutes a b 
    static member  ($) (Sum, b:int<seconds>) = fun D1 D2 a -> minutes_to_seconds a b  

let inline (+) a b :'t = (Sum $ b) D1 D2 a 

let duration = 1<hours> + 2<minutes> + 3<seconds> 

Aber es ist wirklich Hacky, ich würde es nicht empfehlen.

UPDATE

Basierend auf den hier Kommentare sind einige Antworten:

  • Diese Technik verwendet Überlastungen, die zum Zeitpunkt der Kompilierung aufgelöst werden, so dass es während der Laufzeit keine Leistungseinbuße. Es basiert auf dem, was ich vor einiger Zeit in my blog geschrieben habe.

  • Um weitere Überlastungen fügen Sie mehr Dummy-Parameter hinzugefügt werden (D3, D4, ...) und schließlich, wenn Sie einige Überlastungen hinzufügen entscheiden, die mit einer bestehenden Konflikt Sie einen ternären Operator verwenden, müssen möglicherweise (?<-) oder ein Funktionsaufruf mit expliziten statischen Member-Constraints. Here's a sample code.

  • Ich denke, ich würde es nicht verwenden, da es viele Hacks (eine Dummy-Überladung und 2 Dummy-Typen) erfordert und der Code weniger lesbar wird. Wenn F # schließlich mehr Unterstützung für Inline-Funktionen auf der Basis von Überladungen bietet, würde ich es definitiv in Betracht ziehen.

  • Phil Trelford's technique (in Reeds Antwort erwähnt) funktioniert zur Laufzeit, eine dritte Option wäre, Phantom-Typen zu verwenden, kann es weniger Hacks erfordern.

Fazit

Wenn ich zwischen allen Alternativen zu wählen hätte, würde ich diese Technik verwenden, aber noch deutlicher an der Aufrufstelle zu sein, ich meine, ich würde Konvertierungsfunktionen wie minutes, seconds und diese Weise definieren an der Aufrufstelle würde ich schreiben:

let duration = seconds 1<hours> + seconds 2<minutes> + 3<seconds> 

Und dann diese Konvertierungsfunktionen zu definieren wir Überlastungen verwenden würden, aber es wäre weniger hacky als Neudefinition einen exis Binärer Operator.

+0

Wow, das ist großartig! Ich habe jedoch ein paar Fragen: 1. Warum empfiehlst du es nicht? 2. Verliere ich die Typensicherheit? 3. Wenn du wählen müsstest, würdest du lieber mit Phil Trelforts Laufzeitlösung gehen oder das? 4. Was macht $ eigentlich? Danke! – user3323923

+0

Btw, ich wünschte, ich könnte zwei Antworten akzeptieren. – user3323923

+0

@ user3323923, Gustavo definiert einen Operator '$' mit Klammern '($)'. @Gustavo, ich zweitens die Frage, warum würdest du es nicht benutzen? Es scheint eine legitime Lösung zu sein. –

5

Dies ist nicht direkt in F # möglich. Es gibt keine Möglichkeit, die "automatische Konvertierung" direkt auszuführen, ohne die Konvertierung für die Typen anzugeben. Sie müssten Ihre Konvertierungsfunktionen explizit aufrufen (seconds_per_minute, usw.).

Allerdings zeigte Phil Trelford einen Mechanismus, mit dem Sie create runtime classes which do support this, obwohl mit etwas anderer Syntax. Mit seiner Art, könnte man schreiben:

let duration = 1.0 * SI.hours + 2.0 * SI.minutes + 3.0 * SI.seconds