Ostatnio miałem przyjemność napisać program Haskell, który mógłby wykryć, czy NegativeLiterals
rozszerzenie jest włączone. Wymyśliłem następujące:
data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$1==u(-1)
Zostanie wydrukowany True
normalnie i False
inaczej.
Teraz miałem tyle radości, robiąc to, że rozszerzam wyzwanie na was wszystkich. Jakie inne rozszerzenia języka Haskell możesz złamać?
Zasady
Aby złamać określone rozszerzenie językowe, musisz napisać program Haskell, który kompiluje zarówno z rozszerzeniem językowym, jak i bez niego (ostrzeżenia są w porządku) i wyświetla dwie różne wartości inne niż błędy po uruchomieniu z rozszerzeniem językowym i wyłączeniu go (poprzez dodanie No
prefiksu do rozszerzenie języka). W ten sposób powyższy kod może zostać skrócony do:
data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$u(-1)
który drukuje 1
i -1
.
Każda metoda używana do złamania rozszerzenia musi być specyficzna dla tego rozszerzenia. Mogą istnieć sposoby arbitralnego wykrywania, które flagi kompilatora lub rozszerzenia językowe są włączone, jeśli takie metody nie są dozwolone. Możesz włączyć dodatkowe rozszerzenia językowe lub zmienić optymalizację kompilatora, używając -O
bez żadnych opłat do liczby bajtów.
Rozszerzenia językowe
Nie można złamać każde rozszerzenie języka, które nie mają No
odpowiednika (np Haskell98
, Haskell2010
, Unsafe
, Trustworthy
, Safe
), ponieważ te nie są objęte warunkami opisanymi powyżej. Każde inne rozszerzenie języka jest uczciwą grą.
Punktacja
Otrzymasz jeden punkt za każde rozszerzenie językowe, które złamałeś jako pierwszy, i jeden dodatkowy punkt za każde rozszerzenie językowe, dla którego masz najkrótsze (mierzone w bajtach) pęknięcie. W przypadku drugiego punktu więzi zostaną zerwane na korzyść wcześniejszych wniosków. Wyższy wynik jest lepszy
Nie będziesz w stanie zdobyć punktu za pierwsze zgłoszenie NegativeLiterals
lub QuasiQuotes
dlatego, że już je złamałem i umieściłem w treści postu. Będziesz jednak mógł zdobyć punkt za najkrótsze pęknięcie każdego z nich. Oto mój trzaskQuasiQuotes
import Text.Heredoc
main=print[here|here<-""] -- |]
źródło
NondecreasingIndentation
z oczywistych powodówWait, what language extension is this?
może coś zupełnie innego.RelaxedPolyRec
, ponieważ kompilator na tyle starożytny, że rzeczywiście obsługuje jego wyłączenie. (Ta opcja pozostała z dokumentacją przez kilka lat po tym, jak przestała coś robić).Odpowiedzi:
MagicHash, 30 bajtów
-XMagicHash wyjścia 1, -XNoMagicHash wyjścia 2
MagicHash pozwala na zakończenie nazw zmiennych w
#
. Dlatego z rozszerzeniem, to definiuje dwie funkcjey#
, ax#
których każda ma wartość i powrócić stałej2
lub1
.x#x
zwróci 1 (ponieważ jestx#
stosowany do1
)Bez rozszerzenia definiuje to jedną funkcję,
#
która pobiera dwa argumenty i zwraca2
.x#a=1
To wzór, który nigdy nie zostanie osiągnięty. Następniex#x
jest1#1
, która zwraca 2.źródło
MagicHash
nie pozwala na ciągłe hashowanie. Dziwne!CPP,
3320 bajtówDrukuje
0
z-XCPP
i1
o-XNoCPP
.Za
-XCPP
pomocą ukośnika\
przed nowym wierszem usuwa się nowy wiersz, w ten sposób kod staje sięmain=print$0-- +1
i0
jest drukowany tylko wtedy, gdy+1
jest teraz częścią komentarza.Bez flagi komentarz jest ignorowany, a druga linia jest analizowana jako część poprzedniej linii, ponieważ jest wcięta.
Poprzednie podejście z
#define
Drukuje również
0
z-XCPP
i1
z-XNoCPP
.źródło
NumDecimals, 14 bajtów
-XNumDecimals drukuje
10
. -XNoNumDecimals drukuje10.0
.źródło
BinaryLiterals, 57 bajtów
-XBinaryLiterals wypisuje pojedynczy znak nowej linii. -XNoBinaryLiterals drukuje a
1
.Jestem pewien, że jest na to lepszy sposób. Jeśli znajdziesz, prześlij go.
źródło
b
jako funkcji (więc żaden plik binarny nie staje sięb(0, 1)
, ale staje się plik binarny0b1
)?Monomorfizm Ograniczenie + 7 innych, 107 bajtów
Używa TH, która wymaga flagi
-XTemplateHaskell
przez cały czas.Plik T.hs, 81 + 4 bajty
Główny, 22 bajty
Kompilacja z flagą MonomorphismRestriction wymusza typ
p
na,Integer -> Integer -> Integer
a zatem generuje następujące dane wyjściowe:Kompilacja z flagą NoMonomorphismRestriction pozostawia typ
p
na najbardziej ogólny, tj.Num a => a->a->a
- tworzenie czegoś w rodzaju (skróconoVarT
nazwya
):Wypróbuj je online!
Alternatywy
Ponieważ powyższy kod po prostu wypisuje typ
p
, można to zrobić za pomocą wszystkich flag, które w jakiś sposób wpływają na sposób, w jaki Haskell określa typy. Podam tylko flagę i tym, co zastąpić funkcję,p
aw razie potrzeby dodatkowe flagi (oprócz-XTemplateHaskell
):OverloadedLists, 106 bajtów
Dodatkowo potrzebuje
-XNoMonomorphismRestriction
:Albo
p :: [a]
albop :: IsList l => l
, spróbuj je online!OverloadedStrings, 106 bajtów
Dodatkowo potrzebuje
-XNoMonomorphismRestriction
:Albo
p :: String
albop :: IsString s => s
, spróbuj je online!PolyKinds, 112 bajtów
Wynika to całkowicie z @CsongorKiss:
Albo
P :: P a
alboP :: forall k (a :: k). P a
, spróbuj je online!MonadCompstandingions, 114 bajtów
Albo
p :: [a] -> [a]
albop :: Monad m => m a -> m a
, spróbuj je online!NamedWildCards, 114 bajtów
Ten został znaleziony przez @Laikoni, wymaga dodatkowo
-XPartialTypeSignatures
:Oba mają typ zapisu (
p :: a -> a
), ale GHC generuje różne nazwy zmiennych, wypróbuj je online!ApplicativeDo, 120 bajtów
Albo
p :: Monad m => m a -> m a
albop :: Functor f => f a -> f a
, spróbuj je online!OverloadedLabels, 120 bajtów
Wymaga to dodatkowej flagi
-XFlexibleContexts
:Albo typy jak
p :: a -> b -> b
lubp :: IsLabel "id" (a->b) => a -> b
, spróbuj je online!źródło
OverloadedStrings
lubOverloadedLists
na pewno i prawdopodobnie także inni ...PolyKinds
: Wypróbuj online!NamedWildCards
: Wypróbuj online! (Wymaga-XPartialTypeSignatures
)CPP,
2725Wypróbuj online!
Wydruki
()
dla-XCPP
i1
dla-XNoCPP
Poprzednia wersja:
Wypróbuj online!
Drukuje
[1]
z-XCPP
lub w[1,2]
inny sposób.Kredyty: Jest to inspirowane odpowiedzią Laikoni, ale zamiast
#define
tego po prostu używa komentarzy C.źródło
ScopedTypeVariables,
162113 bajtów-XScopedTypeVariables drukuje
""
(pusty), -XNoScopedTypeVariables drukuje"[()]"
.Edycja: zaktualizowane rozwiązanie dzięki przydatnym sugestiom w komentarzach
źródło
"T"
można to po prostu zastąpić""
.T
z()
. Aby uniknąć konieczności jego definiowania. Wypróbuj online!show
można zmienić na drukforall
pozwoli Ci zaoszczędzić kilka bajtów. Wątpię jednak, aby jakiekolwiek rozwiązanie wymagające dodatkowych instancji miało dużą nadzieję na zwycięstwo.MonoLocalBinds, GADTs lub TypeFamilies,
3632 bajtówEDYTOWAĆ:
(1.0,1)
.Z każdą z flag -XMonoLocalBinds , -XGADTs lub -XTypeFamilies , drukuje
(1.0,1.0)
.MonoLocalBinds
Przedłużenie istnieje, aby zapobiec trochę nieintuicyjne typu wnioskowanie wyzwalany przez GADTs i rodzin typu. W związku z tym to rozszerzenie jest automatycznie włączane przez dwie pozostałe.-XNoMonoLocalBinds
, ta sztuczka zakłada, że nie.Podobnie jak jego bardziej znany kuzyn, ograniczenie monomorfizmu,
MonoLocalBinds
działa poprzez zapobieganie polimorficzności niektórych wartości (w lokalnych wiązaniach takich jaknajwyraźniej może się zdarzyć na najwyższym poziomie). Pomimo tego, że został stworzony do wnioskowania o typie rozsądniejszego, reguły dotyczące jego wyzwalania są, jeśli to możliwe, jeszcze bardziej owłosione niż MR.let
lubwhere
, a więc nazwaBez rozszerzenia powyższy program podaje typ
f :: Num a => a -> a
, pozwalającf pi
domyślnie na aDouble
if 0
naInteger
.f :: Double -> Double
if 0
musi również zwrócić aDouble
.a=0
jest potrzebne do uruchomienia przepisy techniczne:a
jest trafiony przez ograniczenie monomorfizm ia
jest zmienna wolna odf
, co oznacza, żef
jest grupa wiążący nie jest w pełni uogólnione , co oznacza,f
nie jest zamknięty , a zatem nie stać polimorficzny.źródło
OverloadedStrings,
654832 bajtówKorzystając z RebindableSyntax, skorzystaj z naszej własnej wersji fromString, aby zamienić dowolny ciąg literału na
"y"
.Musi zostać skompilowany z
-XRebindableSyntax -XImplicitPrelude
.Bez
-XOverloadedStrings
nadruków""
; z nadrukami"y"
.Dopiero teraz uderzyło mnie, że ta sama technika działa z (np.) OverloadedLists:
OverloadedLists, 27 bajtów
Musi zostać skompilowany z
-XRebindableSyntax -XImplicitPrelude
.Bez
-XOverloadedLists
nadruków[0]
; z nadrukami[1,0]
.źródło
fromString a=['y']
.print "n"
można również upuścić.="y"
, ale=['y']
działa dobrze!n
zprint"n"
-XImplicitPrelude
po,RebindableSyntax
aby uniknąć linii importu.BangPatterns, 32 bajty
-XBangPatterns drukuje,
1
podczas gdy -XNoBangPatterns drukuje0
.Wykorzystuje to fakt, że flaga BangPatterns pozwala na dodawanie adnotacji do wzorców w
!
celu wymuszenia oceny WHNF, w takim przypadku9!1
użyje definicji najwyższego poziomu(!)=seq
. Jeśli flaga nie jest włączona,f!_
definiuje nowego operatora(!)
i przesłania definicję najwyższego poziomu.źródło
ApplicativeDo, 104 bajty
Wypróbuj online!
Z
ApplicativeDo
, to drukujeBez tego drukuje
ZipList
jest jednym z niewielu typów w bibliotekach podstawowych z instancją dla,Applicative
ale nie dlaMonad
. Gdzieś czają się krótsze alternatywy.źródło
Ścisłe,
87 8482 bajtów-5 bajtów dzięki dfeuer !
Może być mniej z
BlockArguments
oszczędzaniem parens wokół\_->print 1
:Uruchomienie tego z -XStrict spowoduje wydrukowanie a,
1
natomiast uruchomienie go z -XSoStrict spowoduje wydrukowanie a0
. Wykorzystuje to, że Haskell domyślnie jest leniwy i nie musi oceniać,error""
ponieważ już wie, że wynikiem będzie0
dopasowanie pierwszego argumentu(!)
, to zachowanie można zmienić za pomocą tej flagi - zmuszając środowisko wykonawcze do oceny obu argumentów.Jeśli nic nie jest drukowane w jednym przypadku, możemy sprowadzić go do 75 bajtów, zastępując główny przez (także niektóre bajty wyłączone przez dfeuer ):
StrictData,
106 9993 bajtów-15 bajtów dzięki dfeuer !
To w zasadzie robi to samo, ale zamiast tego działa z polami danych:
Drukuje
1
z flagą -XStrictData i0
z -XNoStrictData .Jeśli nic nie jest drukowane w jednym przypadku, możemy zmniejszyć go do 86 bajtów, zastępując główny przez (19 bajtów przez dfeuer ):
Uwaga: wszystkie rozwiązania wymagają
TypeApplications
zestawu.źródło
pure()
.D{}
sztuczka jest całkiem fajna! Ogoliłem jeszcze jeden, używającPartialTypeSignatures
zamiastScopedTypeVariables
:)-XBlockArguments
:main=catch @ErrorCall(p$seq(D$error"")1)\_->p 3
ApplicativeDo, 146 bajtów
Drukuje 1, gdy włączona jest opcja ApplicativeDo, w przeciwnym razie 0
Wypróbuj online!
źródło
Applicative
iShow
zapisać przy użyciu składni rekordu, zobacz to .BinaryLiterals,
3124 bajtówEdytować:
b12
zmiennej.Dostosowanie metody H.PWiz , unikanie wystąpienia funkcji.
0b12
leksykon as , wypisywanie0
b12
0 + 1 =1
.0b12
leksykon as , wypisywanie0b1
2
1 + 2 =3
.źródło
ExtendedDefaultRules,
5453 bajtówDrukuje
()
z-XExtendedDefaultRules
i0
o-XNoExtendedDefaultRules
.Ta flaga jest domyślnie włączona w GHCi, ale nie w GHC, co ostatnio spowodowało pewne zamieszanie , chociaż BMO szybko było w stanie pomóc.
Powyższy kod jest golfową wersją przykładu w Podręczniku użytkownika GHC, w którym wyjaśniono domyślny typ w GHCi .
-1 bajt dzięki Ørjan Johansen !
źródło
toEnum 0::Num a=>Enum a=>a
.PartialTypeSignatures
:main=print(toEnum 0::_=>Num a=>a)
. Ponadto Twój link TIO jest nieaktualny.RebindableSyntax , 25 bajtów
Czytałem niedawno opublikowany Przewodnik po rozszerzeniach GHC, kiedy zauważyłem łatwy, którego jeszcze nie pamiętałem.
-1
.1
.Wymaga również
-XImplicitPrelude
lub alternatywnieimport Prelude
w samym kodzie.-XRebindableSyntax
zmienia zachowanie niektórych cukrów syntaktycznych Haskella, aby umożliwić jego przedefiniowanie.-1
jest cukrem syntaktycznym dlanegate 1
.negate
jestPrelude.negate
, ale z rozszerzeniem jest to „którekolwieknegate
jest w zasięgu w momencie użycia”, które jest zdefiniowane jakoid
.Prelude
modułu, automatycznie wyłącza zwykły domyślny import tego modułu, ale tutaj potrzebne są innePrelude
funkcje (jakprint
), więc można je ponownie włączyć-XImplicitPrelude
.źródło
Ścisłe, 52 bajty
-XStrict
-XNoStrict
Dzięki
-XStrict
drukuje()
dodatkowy czas.Dzięki @Sriotchilism O'Zaic za dwa bajty.
źródło
StrictData, 58 bajtów
(Linki są nieco nieaktualne; zostaną naprawione).
-XNoStrictData
-XStrictData
Wymaga
MagicHash
(abyśmy mogli importowaćGHC.Exts
zamiastUnsafe.Coerce
) i-O
(absolutnie wymagane, aby umożliwić rozpakowanie małych ścisłych pól).Za pomocą
-XStrictData
, drukuje 3. W przeciwnym razie drukuje wartość całkowitą wskaźnika (prawdopodobnie oznaczonego) do wstępnie przydzielonej kopii3::Integer
, która nie może być równa 3.Wyjaśnienie
Będzie to nieco łatwiejsze do zrozumienia z niewielkim rozszerzeniem, opartym na domyślnym typie. Dzięki podpisom możemy usunąć dodatek.
Równoważnie
Dlaczego kiedykolwiek drukuje 3? To wydaje się zaskakujące! Cóż, małe
Integer
wartości są reprezentowane bardzo podobnie jakInt
s, które (przy ścisłych danych) są reprezentowane podobnie jakD
s. W końcu ignorujemy znacznik wskazujący, czy liczba całkowita jest mała, czy duża dodatnia / ujemna.Dlaczego nie można wydrukować 3 bez rozszerzenia? Pomijając wszelkie przyczyny związane z układem pamięci, wskaźnik danych z małymi bitami (2 najniższe dla 32-bitów, 3 najniższe dla 64-bitów) 3 musi reprezentować wartość zbudowaną z trzeciego konstruktora. W takim przypadku wymagałoby to ujemnej liczby całkowitej.
źródło
UnboxedTuples, 52 bajty
Wymaga
-XTemplateHaskell
. DrukujeConE GHC.Prim.(##)
z -XUnboxedTuples iUnboundVarE ##
z -XNoUnboxedTuples .źródło
-XTemplateHaskell
?OverloadedLists, 76 bajtów
Z opcją -XOverloadedLists drukuje
[()]
. Z opcją -XNoOverloadedLists drukuje[]
Wymaga to dodatkowych flag:
-XFlexibleInstances
,-XIncoherentInstances
źródło
HexFloatLiterals ,
4925 bajtów-24 bajty dzięki Ørjan Johansen.
Drukuje
0.0
z-XHexFloatLiterals
i0
o-XNoHexFloatLiterals
.Nie ma linków TIO, ponieważ w ghc 8.4.1 dodano HexFloatLiterals, ale TIO ma ghc 8.2.2.
źródło
main|(.)<-seq=print$0x0.0
pozwala uniknąć ukrywania importu.main|let _._=0=print$0x0.0
może być łatwiejsze dla poliglota.ScopedTypeVariables, 37 bajtów
Wymaga to również
UnicodeSyntax
,PartialTypeSignatures
,GADTs
, iExplicitForAll
.Wypróbuj online (bez rozszerzenia)
Wypróbuj online (z rozszerzeniem)
Wyjaśnienie
Podpisy typu częściowego służą jedynie do zapisywania bajtów. Możemy je wypełnić w następujący sposób:
Z o zakresie zmiennych typu,
a
w rodzaju1
jest ograniczone byća
w rodzajumain
, który z kolei jest przymocowany do byćFloat
. Bez zmiennych typu o zasięgu1
domyślnie wpisuje sięInteger
. PonieważFloat
iInteger
wartości są pokazane inaczej, możemy je rozróżnić.Dzięki @ ØrjanJohansen za aż 19 bajtów! Uświadomił sobie, że znacznie lepiej jest wykorzystać różnicę między
Show
instancjami różnych typów liczbowych niż różnice w ich arytmetyce. Uświadomił sobie również, że można pozostawić typmain
„dwuznacznie składniowo”, ponieważ ograniczenie faktycznie go ujednoznacznia. Pozbycie się funkcji lokalnej uwolniło mnie również do usunięcia podpisu typu dlamain
(przeniesienie go do RHS), aby zaoszczędzić pięć kolejnych bajtów.źródło
DeriveAnyClass,
121113 bajtówDzięki dfeuer za całkiem sporo bajtów!
Drukuje -XDeriveAnyClass,
1
podczas gdy -XNoDeriveAnyClass drukujeM 0
.Wykorzystuje to fakt, że DeriveAnyClass jest strategią domyślną, gdy zarówno DeriveAnyClass, jak i GeneralizedNewtypeDeriving są włączone, jak widać z ostrzeżeń. Ta flaga chętnie generować puste implementacje wszystkich metod, ale GeneralizedNewtypeDeriving jest rzeczywiście mądry wystarczy użyć wdrożenie instrumentu bazowego Type i ponieważ
Int
jestNum
ona nie zawiedzie w tej sprawie.Jeśli nic nie zostanie wydrukowane, jeśli flaga jest włączona, zastąpienie
main
jej następującą wartością będzie 109 bajtów :źródło
runhaskell
, to faktycznie drukuje,M 1
z-XDeriveAnyClass
powodu lenistwa ...1
:)PostfixOperators, 63 bajty
Wypróbuj online (bez rozszerzenia)
Wypróbuj online (z rozszerzeniem)
To jest wycięta wersja poliglota Hugs / GHC, który napisałem . Zobacz ten post dla wyjaśnienia. Dzięki @ ØrjanJohansen za uświadomienie sobie, że mogę użyć
id
zamiast operatora niestandardowego, oszczędzając cztery bajty.źródło
id
można użyć zamiast!
.DeriveAnyClass, 104 bajty
Wypróbuj online (bez rozszerzenia)
Wypróbuj online (z rozszerzeniem)
Wymaga również
GeneralizedNewtypeDeriving
.źródło
StrictData, 97 bajtów
Wypróbuj online (bez ścisłych danych)
Wypróbuj online (ścisłe dane)
Wymaga również
DeriveGeneric
.źródło
UnicodeSyntax, 33 bajty
Wypróbuj online!
źródło
TemplateHaskell,
14091 bajtówWłaśnie skopiowane z mauke z niewielkimi modyfikacjami. Nie wiem co się dzieje.
-49 bajtów dzięki Ørjan Johansen.
Wypróbuj online!
źródło
$(...)
(bez spacji) to składnia oceny szablonu, gdy TH jest włączone, aTupE[]
(„pusta krotka”) daje()
. UżywanieShow
może działać dobrze dla poliglota, chociaż w przypadku tego konkretnego wyzwania czuję się trochę źle, definiując wartość do wydrukowania jako pusty ciąg ...Monomorfizm Ograniczenie,
3129 bajtówEdytować:
-X Odbitki monomorfizmu
0
. -XNoMonomorphismRestriction prints18446744073709551616
.f
muszą być tego samego typu, więc program drukuje2^2^6 = 2^64
jako 64-bitowyInt
(na platformach 64-bitowych), co powoduje przepełnienie0
.2^64
jako bignumInteger
.źródło
f=(2^);main=print$f$f(64::Int)
że uratuje bajt. Ale to nie zakończy się realistycznie64=2^6
, co oszczędza kolejny bajt.ScopedTypeVariables,
11997 bajtówWłaśnie skopiowane z mauke z niewielkimi modyfikacjami.
Obecnie istnieją dwie inne odpowiedzi dla ScopedTypeVariables: 113 bajtów Csongor Kiss i 37 bajtów dfeuer . To zgłoszenie różni się tym, że nie wymaga innych rozszerzeń Haskell.
-22 bajty dzięki Ørjan Johansen.
Wypróbuj online!
źródło
IO()/print
sztuczka nie działa w poliglocie).Num
. Myślę, żeclass(Show a,Floating a)=>K a where{k::a->String;k=pure$ show(f pi)where f=id::a->a};
powinien działać, wygodnie go używaćFloat
iDouble
wyświetlaćpi
z różną precyzją.