2017-02-15 6 views
5

Wie ich die Methode Zero in dem folgenden Code in einer solchen Art und Weise tun außer Kraft setzen, die ich Euro(0) für die Definiton in der type EuroWie richtig überschreiben ich ein eingeschränktes Verfahren

[<AbstractClass>] 
type Currency() = 
    abstract member Zero<'T when 'T :> Currency > : unit -> 'T 

type Euro (value: int) = 
    inherit Currency() 
    member this.Value = value 
    override this.Zero() = Euro(0) :> _ 
+1

'Zero' ist eine generische Methode und muss einen Wert für jeden Subtyp von' Currency' zurückgeben, den der Aufrufer auswählt, damit Sie ihn nicht in Ihrem 'Euro'-Typ reparieren können. Im Moment könnte ein Anrufer 'let c = new Euro (0):> Currency; c.Zero () '. Können Sie 'T' in die' Currency' Klasse verschieben? – Lee

+1

Ist das für alle praktischen Zwecke ein DU? Nein? –

+2

@HelgeReneUrholm: Nein, in diesem Fall gibt es ein offenes Universum von Typen. Sie verlieren erschöpfende Übereinstimmungen, können aber neue Subtypen hinzufügen, ohne sie neu zu kompilieren (oder in anderen Dateien/Assemblies usw.). – scrwtp

Antwort

7

Haben Sie heben versucht, die generische zurückgeben Einschränkung auf Klassenebene?

[<AbstractClass>] 
type Currency<'T when 'T :> Currency<'T>>() = 
    abstract member Zero : unit -> 'T 

type Euro (value: int) = 
    inherit Currency<Euro>() 
    member this.Value = value 
    override this.Zero() = Euro(0) 

Obwohl sich selbst verweisende Generika immer seltsam mir scheint, dies ist, wie es in getan werden würde, beispielsweise C#.

+0

Oh mein Gott. Ich bin ohnmächtig! Danke. PS: Ich habe auch eine andere Lösung. – robkuz

+2

Coolio ... was ist die alternative Lösung? Machen Sie das Mitglied statisch und verzichten Sie auf die Vererbung? – pblasucci

+0

@pblasucci fwiw, ich habe eine andere Lösung hinzugefügt. – Yawar

1

In F # gibt es auch die Methode "Roll your own typeclass". Grundsätzlich werden die Member (Members und Static) des abstrakten Typs die Felder eines 'typeclass'-Records und die Werte dieses Records sind typeclass-Instanzen. Sie können einen ‚Euro‘ Instanz, einen ‚Dollar‘ So und so weiter:

module Currency = 
    type t<[<Measure>] 'a> = 
    { zero : decimal<'a>; from : decimal -> decimal<'a> } 

    /// Helper function to easily create typeclass instances for any 
    /// currency. 
    let make<[<Measure>] 'a> (curr_unit : decimal<'a>) : t<'a> = 
    { zero = curr_unit - curr_unit; from = ((*) curr_unit) } 

    [<Measure>] type euro 
    let euro : t<euro> = make 1m<euro> 

    [<Measure>] type dollar 
    let dollar : t<dollar> = make 1m<dollar> 

Das Besondere an F # ist, dass der Typ-Parameter, die tatsächlich auf jede typeclass Instanz übergeben wird, kann ein Maß Typ sein, was für Währungen angemessen ist.

+0

Dies ist OK, solange Sie nicht diese verschiedenen Arten in eine generische Liste setzen möchten – robkuz

+0

@robkuz: Ich würde mit Maßeinheiten in erster Linie gegangen, aber Ihre Anwendung erfordert, verschiedene Währungen in eine Liste zu setzen, ist das richtig? –

+0

@AntonSchwaighofer yup – robkuz

Verwandte Themen