Eine wenig bekannte Tatsache ist, dass Haskell eine dynamisch typisierte interpretierte Sprache wird, wenn Sie genügend Spracherweiterungen (ghc) aktivieren! Das folgende Programm implementiert beispielsweise das Hinzufügen.
{-# Language MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
data Zero
data Succ a
class Add a b c | a b -> c
instance Add Zero a a
instance (Add a b c) => Add (Succ a) b (Succ c)
Das sieht nicht mehr wirklich nach Haskell aus. Zum einen operieren wir nicht über Objekte, sondern über Typen. Jede Nummer ist ein eigener Typ. Anstelle von Funktionen haben wir Typklassen. Die funktionalen Abhängigkeiten ermöglichen es uns, sie als Funktionen zwischen Typen zu verwenden.
Wie rufen wir unseren Code auf? Wir benutzen eine andere Klasse
class Test a | -> a
where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
=> Test a
Dies setzt den Typ test
auf den Typ 4 + 3. Wenn wir dies in ghci öffnen, werden wir feststellen, dass test
es sich tatsächlich um Typ 7 handelt:
Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))
Aufgabe
Ich möchte, dass Sie eine Klasse implementieren, die zwei Peano-Ziffern (nicht negative Ganzzahlen) multipliziert. Die Peano-Ziffern werden im obigen Beispiel mit denselben Datentypen erstellt:
data Zero
data Succ a
Und Ihre Klasse wird auf die gleiche Weise wie oben bewertet. Sie können Ihre Klasse nach Belieben benennen.
Sie können beliebige GHC-Spracherweiterungen kostenlos für Bytes verwenden.
Testfälle
In diesen Testfällen wird davon ausgegangen, dass Ihre Klasse benannt M
ist. Sie können sie auch anders benennen , wenn Sie möchten.
class Test1 a| ->a where test1::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)=>Test1 a
class Test2 a| ->a where test2::a
instance (M Zero (Succ (Succ Zero)) a)=>Test2 a
class Test3 a| ->a where test3::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ Zero) a)=>Test3 a
class Test4 a| ->a where test4::a
instance (M (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))) (Succ (Succ (Succ Zero))) a)=>Test4 a
Ergebnisse
*Main> :t test1
test1
:: Succ
(Succ
(Succ
(Succ
(Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))
*Main> :t test2
test2 :: Zero
*Main> :t test3
test3 :: Succ (Succ (Succ (Succ Zero)))
*Main> :t test4
test4
:: Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))))))))
Lässt sich von der Eingabe des technischen Interviews inspirieren
quelle
Antworten:
130121 Bytes-9 Bytes dank Ørjan Johansen
Probieren Sie es online aus!
Dies definiert geschlossene Typfamilien für Addition
(+)
und Multiplikation(*)
. Anschließend wird eine Typklasse(#)
definiert, die die(*)
Typfamilie zusammen mit einer Gleichheitsbeschränkung verwendet, um von der Welt der Typfamilien in die Welt des Typklassenprologs zu konvertieren.quelle
Zero
durchz
.+
nützlich?139 Bytes
Probieren Sie es online aus!
Definiert einen Typoperator
*
. Entspricht dem Prolog-Programm:Potato44 und Hat Wizard haben jeweils 9 Bytes gespeichert. Vielen Dank!
quelle
f
stattdessen einen General verwenden könnenSucc
.Familienversion, 115 Bytes
Probieren Sie es online aus!
Dies verwendet eine geschlossene Typfamilie wie Potato44 . Außer im Gegensatz zu der anderen Antwort verwende ich nur 1 Typfamilie.
Dies definiert einen Operator für drei Typen. Es implementiert im Wesentlichen
(a*b)+c
. Wann immer wir unser rechtes Argument zur Summe hinzufügen wollen, legen wir es stattdessen in den Akkumulator.Dies verhindert, dass wir überhaupt definieren müssen
(+)
. Technisch gesehen können Sie diese Familie verwenden, um das Hinzufügen zu implementierenKlassenversion, 137 Bytes
Probieren Sie es online aus!
Diese Klassenversion verliert etwas an Boden gegenüber der Familienversion, ist jedoch immer noch kürzer als die kürzeste Klassenversion hier. Es verwendet den gleichen Ansatz wie meine Familienversion.
quelle
Constraint
. Sie sollten also entweder die Spezifikation aktualisieren oder zu dem Formular zurückkehren, das eine Klasse anstelle eines Typensynonyms verwendet. Wenn ich das