Ich baue ein Spielzeug, wo Sie Schaltungen auf einem Gitter zeichnen können und wir ihr Verhalten simulieren können. Ich dachte, es wäre ein lustiges Experiment, über die Dimensionalität des Boards zu abstrahieren und zu versuchen, den Code (typsicher) über alle Board-Dimensionen (2D, 3D, 4D usw.) hinweg arbeiten zu lassen.Abstrahieren über die Dimensionalität der Typen
Ich kann die meiste Arbeit mit GADTs und Nats machen; Angenommen, ich verwende einen Vektor als 2D-Basisabstraktion, können wir jede Dimensionalität darstellen, indem wir sie zusammensetzen;
type family Count t where
Count (Compose _ g) = 1 + (Count g)
Count _ = 0
data Grid (n::Nat) a where
Grid :: f a -> Grid (Count f) a
Das funktioniert in den meisten Fällen (leider die Art Familie erfordert UndecidableInstances)
Damit kann ich ausdrücken, dass Operationen über Gitter in Dimensionalität konsistent bleiben, dh
alter :: Grid n a -> Grid n b
Der schwierige Bit ist, dass ich mir auch erlauben möchte, mich in den Gittern zu bewegen. Ich habe eine Representable-Instanz für Grid geschrieben, die auf dem zugrundeliegenden Representable für Compose
basiert, im Grunde paaren Sie einfach die Repräsentation für jeden Funktor, der zusammengesetzt wird. In meinem Fall sind hier einige Beispiele Darstellungen:
Rep (Grid 2) ~ (Sum Int, Sum Int)
Rep (Grid 3) ~ (Sum Int, (Sum Int, Sum Int))
Rep (Grid 3) ~ (Sum Int, (Sum Int, (Sum Int, Sum Int)))
Und so weiter.
übernehmen Sie auch, dass wir Index in eine Grid durch einen Index daneben als Speicher zu halten comonad type IGrid n a = (Rep (Grid n), Grid n a)
ich einige Funktionen geschrieben haben, die sich um in einer bestimmten Dimension bewegen. Wenn eine Funktion den Fokus auf der y-Achse verschiebt, können wir diese Funktion immer noch auf jeder Dimensionalität mit mindestens 2 Dimensionen aufrufen:
z.
moveUp :: (n >= 2) => IGrid n a -> IGrid n a
Dies ist machbar und einfach, wenn n == 2, aber für höhere Dimensionen am einfachsten ist es wahrscheinlich durch die Förderung eines niedrigeren Dimensionalität Index in eine höhere (padding unbekannt dimensional coords mit mempty) zu implementieren, so dass ich verwenden können, seek :: Rep (Grid n) -> Grid n a -> Grid n a
richtig.
promote :: (m <= n) => Rep (Grid m) -> Rep (Grid n)
Dann kann ich nur einen bestimmten Index zu jedem dim fördern, bevor es mit:
moveBy :: Rep (Grid n) -> IGrid n a -> IGrid n a
moveBy m (rep, grid) = (rep <> m, grid)
moveAround :: IGrid n a -> IGrid n a
moveAround grid = grid
& moveBy (promote (Sum 3, Sum 2))
& moveBy (promote (Sum 1))
Die meisten meiner Versuche, zentriert um eine typeclass verwenden und es über bestimmte Nats und mit viel Art der Umsetzung Behauptungen. Ich habe war in der Lage, einen Index um ein oder zwei endliche Ebenen zu fördern, kann aber den allgemeinen Fall nicht herausfinden.
Ich habe versucht, diese promote
Funktion für einen Monat oder zwei jetzt zu schreiben, von Zeit zu Zeit zurück zu kommen und es scheint möglich, aber ich kann es einfach nicht herausfinden. Jede Hilfe würde sehr geschätzt werden. Die Verwendung von Nats und der Singletons lib ist in Ordnung, wenn das der richtige Weg ist :)
Danke, dass du dir die Zeit genommen hast, mein Dilemma zu lesen!
Wo sind die Typstufen 'Nat' und' Representable', mit denen Sie arbeiten? Ist [dies das "Darstellbare"] (https://hackage.haskell.org/package/representable-functors--3.2.0.2/docs/Data-Functor-Representable.html)? – Cirdec
Nats sind GHC.TypeLits, Repräsentierbar ist Data.Functor.Rep; dieses von adjunctions: http://hackage.haskell.org/package/adjunctions-4.3/docs/Data-Functor-Rep.html –
Ich glaube nicht, dass eine 'Darstellbare' Instanz für' Grid' existiert. Betrachte diesen Wert 'Grid (Op (const True)) :: Grid 0a' wo' Op' der nicht darstellbare kontravariante Funktor 'newtype Op r a = Op (a -> r)' ist. – Cirdec