Ich möchte oft eine Funktion auf die Werte innerhalb einer Variante anwenden, so dass das Ergebnis der Funktion den gleichen Typ wie die Eingabe hat. Wie kann ich die Typen trainieren? Hier ist meine aktuelle Versuch:Map eine Teilmenge einer polymorphen Variante
module T : sig
type generic =
[ `Foo of int
| `Bar of int ]
val map : (int -> int) -> ([< generic] as 'a) -> 'a
(* val a : [`Foo of int] *)
end = struct
type generic =
[ `Foo of int
| `Bar of int ]
let map fn = function
| `Foo x -> `Foo (fn x)
| `Bar x -> `Bar (fn x)
let map : (int -> int) -> ([< generic] as 'a) -> 'a = Obj.magic map
(*
let a : [`Foo of int] = `Foo 1 |> map succ
let b : [`Bar of int] = `Bar 1 |> map succ
*)
end
Das funktioniert, wie sie ist, aber wenn ich Kommentar- der let a
Linie dann bekomme ich:
Values do not match:
val map : (int -> int) -> [ `Foo of int ] -> [ `Foo of int ]
is not included in
val map : (int -> int) -> ([< generic ] as 'a) -> 'a
Es ist wie a
definieren scheint hat die Art der map
geändert, das scheint ungerade.
Auf der anderen Seite, das nach dem Ende des Moduls setzen funktioniert:
open T
let a : [`Foo of int] = `Foo 1 |> map succ
let b : [`Bar of int] = `Bar 1 |> map succ
Schließlich, wenn ich die Definition von map
zu ändern:
let map : 'a. (int -> int) -> ([< generic] as 'a) -> 'a = Obj.magic map
(dh einer expliziten gerade hinzufügen 'a.
zu Beginn), dann klagt er:
Error: This definition has type (int -> int) -> ([< generic ] as 'a) -> 'a
which is less general than
'b. (int -> int) -> ([< generic ] as 'b) -> 'b
Kann jemand erklären, was vor sich geht? Gibt es einen besseren Weg, dies zu tun? Ich kann eine GADT verwenden, um die Obj.magic
zu vermeiden, aber dann muss ich es an jeden Funktionsaufruf übergeben, den ich vermeiden möchte.
Hinweis über das reale System
In meinem aktuellen Programm habe ich verschiedene Arten Knoten (Area
, Project
, Action
, Contact
, usw.) und verschiedene Operationen gelten für verschiedene Arten, aber einige sind weit verbreitet.
Zum Beispiel kann with_name
jeden Knotentyp umbenennen, aber wenn ich einen Action
umbenenne, dann muss das Ergebnis eine andere Aktion sein. Wenn ich ein [Area | Project | Action]
umbenennen dann muss das Ergebnis ein [Area | Project | Action]
sein usw.
ich ursprünglich ein Tupel verwendet, mit den gemeinsamen Details außerhalb (zB (name * Action ...)
), aber das macht es schwierig für die Nutzer auf die verschiedenen Typen entsprechen (insbesondere Sie sind abstrakt) und einige Merkmale sind Teilmengen gemeinsam (z. B. können nur Project
s und Action
s markiert werden).
Ich lese gerade die "Notiz über das reale System" ... Sie könnten einfach Objekte dafür verwenden, weißt du? Es scheint ziemlich passend zu sein. :) – Drup
Ich gab das auch einen Versuch. Es löst dieses Problem, aber es führt zu anderen Problemen (ich möchte diese Elemente wirklich als reine Daten und als Mustervergleich für sie an mehreren Stellen behandeln). –