Espere, que idioma é esse?

37

Recentemente, tive o prazer de escrever um programa Haskell que pudesse detectar se a NegativeLiteralsextensão estava envolvida. Eu vim com o seguinte:

data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$1==u(-1)

Experimente online!

Isso será impresso Truenormalmente e Falsecaso contrário.

Agora eu me diverti muito fazendo isso, estou estendendo o desafio para todos vocês. Quais outras extensões de idioma Haskell você pode quebrar?

Regras

Para quebrar uma extensão de idioma específica, você deve escrever um programa Haskell que compila com e sem a extensão de idioma (os avisos são bons) e gera dois valores diferentes de erro quando executados com a extensão de idioma e desativados (adicionando o Noprefixo a a extensão do idioma). Dessa maneira, o código acima pode ser reduzido para apenas:

data B=B{u::Integer}
instance Num B where{fromInteger=B;negate _=B 1}
main=print$u(-1)

que imprime 1e -1.

Qualquer método usado para quebrar uma extensão deve ser específico a essa extensão. Pode haver maneiras de detectar arbitrariamente quais sinalizadores do compilador ou LanguageExtensions estão habilitados, se esses métodos não forem permitidos. Você pode ativar extensões de idioma adicionais ou alterar a otimização do compilador usando -Osem nenhum custo a sua contagem de bytes.

Extensões de idioma

Você não pode quebrar qualquer extensão de linguagem que não tem uma Nocontrapartida (por exemplo Haskell98, Haskell2010, Unsafe, Trustworthy, Safe) porque estas não se enquadram nos termos descritos acima. Qualquer outra extensão de idioma é um jogo justo.

Pontuação

Você receberá um ponto por cada extensão de idioma que você é a primeira pessoa a quebrar e um ponto adicional por cada extensão de idioma para a qual você tem o menor crack (medido em bytes). Para o segundo ponto, os laços serão quebrados em favor de envios anteriores. Maior pontuação é melhor

Você não poderá marcar um ponto para a primeira submissão NegativeLiteralsou QuasiQuotesporque eu já os decifrei e os incluí no corpo da postagem. No entanto, você será capaz de marcar um ponto pelo menor crack de cada um deles. Aqui está o meu crackQuasiQuotes

import Text.Heredoc
main=print[here|here<-""] -- |]

Experimente online!

Assistente de Trigo
fonte
3
Eu acho que esta é uma lista de todas as opções válidas
H.PWiz
11
Note-se que o meu comentário acima não incluem NondecreasingIndentation, por razões óbvias
H.PWiz
4
Eu acho que esse título é enganador, pois o único idioma que você pode usar é Haskell. Que tal Wait, what language extension is this?ou algo completamente diferente.
MD XF
11
Estou bastante curioso para saber se é possível decifrar RelaxedPolyRec, para um compilador antigo o suficiente para realmente suportá-lo. (A opção pendurados, com a documentação, para alguns anos depois de ter parado de fazer qualquer coisa.)
dfeuer
11
@dfeuer Olhando para este tíquete , parece que o GHC 6.12.1 suportou desativá-lo.
Ørjan Johansen 8/03

Respostas:

24

MagicHash, 30 bytes

x=1
y#a=2
x#a=1
main=print$x#x

-XMagicHash gera 1, -XNoMagicHash gera 2

MagicHash permite que nomes de variáveis ​​terminem em a #. Portanto, com a extensão, isso define duas funções y#e x#cada uma pega um valor e retorna uma constante 2, ou 1. x#xretornará 1 (porque éx# aplicado a 1)

Sem a extensão, isso define uma função #que recebe dois argumentos e retorna 2. O x#a=1é um padrão que nunca é alcançado. Então x#xé 1#1, que retorna 2.

H.PWiz
fonte
2
Agora estou cantando X Magic Hash ao som de Dance Magic Dance . Espero que você esteja orgulhoso!
TRiG 21/01
Surpreende-me que MagicHashnão permita hashes não seguidos. Esquisito!
dfeuer 6/03
18

CPP, 33 20 bytes

main=print$0-- \
 +1

Imprime 0com -XCPPe 1com-XNoCPP .

Com -XCPP, uma barra \antes de uma nova linha remove a nova linha, assim o código se torna main=print$0-- +1e somente 0é impresso como o+1 agora faz parte do comentário.

Sem a sinalização, o comentário é ignorado e a segunda linha é analisada como parte da linha anterior, porque é recuada.


Abordagem anterior com #define

x=1{-
#define x 0
-}
main=print x

Também imprime 0com -XCPPe 1com-XNoCPP .

Laikoni
fonte
2
Oh Deus, até agora eu pensei que GHC tiraria os comentários de Haskell antes de passar para o PCP.
Cubic
@ Cubic Não é um pré- processador?
Bergi
11
@ Bergi Claro, mas pré- processadores não significa necessariamente "é a primeira coisa a ser executada", especialmente porque o GHC precisa passar primeiro pelo arquivo para encontrar o pragma. Acho que os comentários são mantidos em comentários de documentos e trabalhos similares após a conclusão do CPP.
cúbico
14

Literais binários, 57 bytes

b1=1
instance Show(a->b)where;show _=""
main=print$(+)0b1

-XBinaryLiterals imprime uma única nova linha. -XNoBinaryLiterals imprime a 1.

Estou certo de que existe uma maneira melhor de fazer isso. Se você encontrar um, por favor poste.

H.PWiz
fonte
Você não pode simplesmente definir bcomo uma função (para que nenhum binário se torne b(0, 1), mas binário se torne 0b1)?
NoOneIsHere
12

MonomorfismoRestrição + 7 others, 107 bytes

Isso usa TH, que requer a bandeira o -XTemplateHaskelltempo todo.

Arquivo T.hs, 81 + 4 bytes

module T where
import Language.Haskell.TH
p=(+)
t=reify(mkName"p")>>=stringE.show

Principal, 22 bytes

import T
main=print $t

Compilar com o sinalizador MonomorphismRestriction força o tipo de ppara Integer -> Integer -> Integere, portanto, produz a seguinte saída:

"VarI T.p (AppT (AppT ArrowT (ConT GHC.Integer.Type.Integer)) (AppT (AppT ArrowT (ConT GHC.Integer.Type.Integer)) (ConT GHC.Integer.Type.Integer))) Nothing"

Compilar com a bandeira NoMonomorphismRestriction deixa o tipo dep no mais geral, ie. Num a => a->a->a- produzindo algo como (reduziu os VarTnomes para a):

"VarI T.p (ForallT [KindedTV a StarT] [AppT (ConT GHC.Num.Num) (VarT a)] (AppT (AppT ArrowT (VarT a)) (AppT (AppT ArrowT (VarT a)) (VarT a)))) Nothing"

Experimente-os online!


Alternativas

Como o código acima simplesmente imprime o tipo de p, isso pode ser feito com todos os sinalizadores que de alguma forma influenciam a maneira como Haskell deduz os tipos. Especificarei apenas o sinalizador e com o que substituir a função pe, se necessário, sinalizadores adicionais (além de-XTemplateHaskell ):

OverloadedLists, 106 bytes

Além disso precisa -XNoMonomorphismRestriction:

p=[]

Ou , p :: [a]oup :: IsList l => lexperimentá-los online!

OverloadedStrings, 106 bytes

Além disso precisa -XNoMonomorphismRestriction:

p=""

De qualquer p :: Stringou p :: IsString s => s, experimentá-los online!

PolyKinds, 112 bytes

Isso se deve inteiramente ao @CsongorKiss:

data P a=P 

De qualquer P :: P aou P :: forall k (a :: k). P a, experimentá-los online!

MônadasCompreensões, 114 bytes

p x=[i|i<-x]

De qualquer p :: [a] -> [a]ou p :: Monad m => m a -> m a, experimentá-los online!

NamedWildCards, 114 bytes

Este foi encontrado pelo @Laikoni, além disso requer -XPartialTypeSignatures:

p=id::_a->_a

Ambos têm o tipo save ( p :: a -> a), mas o GHC gera nomes diferentes para as variáveis, experimente-os online!

ApplicativeDo, 120 bytes

p x=do i<-x;pure i

De qualquer p :: Monad m => m a -> m aou p :: Functor f => f a -> f a, experimentá-los online!

OverloadedLabels, 120 bytes

Isso precisa do sinalizador adicional -XFlexibleContexts:

p x=(#id)x
(#)=seq

Escreva como p :: a -> b -> bou p :: IsLabel "id" (a->b) => a -> b, experimente-os online!

ბიმო
fonte
Uma coisa semelhante funciona para outras bandeiras?
precisa saber é o seguinte
Sim, você poderia fazê-lo com OverloadedStringsou OverloadedListscom certeza e provavelmente outros, bem ..
ბიმო
2
Também funciona com PolyKinds: Experimente online!
Csongor Beijo
11
Também parece funcionar NamedWildCards: Experimente online! (Requer -XPartialTypeSignatures)
Laikoni 23/01
10

CPP, 27 25

main=print({-/*-}1{-*/-})

Experimente online!

Imprime ()para -XCPPe 1para-XNoCPP

Versão anterior:

main=print[1{-/*-},2{-*/-}]

Experimente online!

Impressões [1] com-XCPP e de [1,2]outra forma.

Créditos: isso é inspirado na resposta de Laikoni, mas em vez de #definesimplesmente usa comentários em C.

celtschk
fonte
9

ScopedTypeVariables, 162 113 bytes

instance Show[()]where show _=""
p::forall a.(Show a,Show[a])=>a->IO()
p a=(print::Show a=>[a]->IO())[a]
main=p()

-XScopedTypeVariables imprime ""(vazio), -XNoScopedTypeVariables imprime"[()]" .

Edit: solução atualizada graças a sugestões úteis nos comentários

Csongor Kiss
fonte
11
Ah entendo. Geralmente é melhor incluir o seu código no corpo, mas versões não destruídas também são boas. Também estou percebendo que isso "T"pode ser substituído por "".
Assistente de trigo
2
Outra coisa que você pode fazer é substituir o seu tipo de dados Tcom (). Para evitar ter que defini-lo. Experimente online!
Assistente de trigo
11
Boa captura, acabei de perceber que o pragma incoerente pode ser incluído como uma bandeira: Experimente online!
Csongor Beijo
2
Além disso, show pode ser alterado para impressão
H.PWiz
A sintaxe Unicode para foralleconomizará alguns bytes. Duvido que qualquer solução que precise de instâncias extras tenha muita esperança de vencer.
dfeuer 7/03
9

MonoLocalBinds, GADTs ou TypeFamilies, 36 32 bytes

EDITAR:

  • -4 bytes: Uma versão disso foi incorporada à grande cadeia poliglota pela stasoid, que me surpreendeu ao colocar todas as declarações em nível superior. Aparentemente, acionar essa restrição não requer ligações locais reais .
a=0
f b=b^a
main=print(f pi,f 0)
  • Sem extensões , este programa é impresso (1.0,1).
  • Com qualquer um dos sinalizadores -XMonoLocalBinds , -XGADTs ou -XTypeFamilies , ele imprime(1.0,1.0) .

  • o MonoLocalBinds extensão existe para evitar alguma inferência de tipo não intuitiva acionada por GADTs e famílias de tipos. Como tal, esta extensão é ativada automaticamente pelas outras duas.

  • Ele é possível desligá-lo novamente de forma explícita com-XNoMonoLocalBinds , este truque assume que você não.
  • Como seu primo mais conhecido, a restrição de monomorfismo, MonoLocalBindsfunciona impedindo que alguns valores ( em ligações locais como letou where, portanto, o nome aparentemente também possa acontecer em nível superior) sejam polimórficos. Apesar de terem sido criadas para inferência de tipo mais saudável, as regras para quando são acionadas são, se possível, ainda mais peludas que a RM.

  • Sem qualquer extensão, os infere programa acima o tipo f :: Num a => a -> a, permitindo que f pipara o padrão para um Doublee f 0para um Integer.

  • Com as extensões, o tipo inferido se torna f :: Double -> Doubleef 0 também deve retornar um Double.
  • A variável separada a=0é necessária para acionar as regras técnicas: aé atingida pela restrição de monomorfismo e aé uma variável livre de f, o que significa que fo grupo de ligação desse grupo não é totalmente generalizado , o que significa que fnão é fechado e, portanto, não se torna polimórfico.
Ørjan Johansen
fonte
9

OverloadedStrings, 65 48 32 bytes

Aproveitando o RebindableSyntax, use nossa própria versão do fromString para transformar qualquer string literal em "y".

main=print""
fromString _=['y']

Deve ser compilado com -XRebindableSyntax -XImplicitPrelude .

Sem -XOverloadedStringsimpressões ""; com estampas"y".

Além disso, apenas agora me ocorreu que a mesma técnica funciona com (por exemplo) OverloadedLists:

OverloadedLists, 27 bytes

main=print[0]
fromListN=(:)

Deve ser compilado com -XRebindableSyntax -XImplicitPrelude .

Sem -XOverloadedListsimpressões [0]; com impressões [1,0].

felixphew
fonte
11
Você pode encurtar a última linha para fromString a=['y'].
Ørjan Johansen
O espaço print "n"também pode ser descartado.
Laikoni 23/01
@ ØrjanJohansen obrigado! Eu estava tendo problemas ="y", mas =['y']funciona bem!
Felixphew
11
Você pode remover o segundo ndeprint"n"
Wheat Wizard
11
Você também pode usar -XImplicitPreludedepois RebindableSyntaxpara evitar a linha de importação.
dfeuer 6/03
8

BangPatterns, 32 bytes

(!)=seq
main|let f!_=0=print$9!1

-XBangPatterns imprime 1enquanto -XNoBangPatterns imprime 0.

Isso faz com que o sinalizador BangPatterns permita anotar padrões com um !para forçar a avaliação ao WHNF; nesse caso 9!1, usará a definição de nível superior (!)=seq. Se o sinalizador não estiver ativado, f!_define um novo operador (!)e oculta a definição de nível superior.

ბიმო
fonte
7

ApplicativeDo, 104 bytes

import Control.Applicative
z=ZipList
instance Monad ZipList where _>>=_=z[]
main=print$do a<-z[1];pure a

Experimente online!

Com ApplicativeDo, isso imprime

ZipList {getZipList = [1]}

Sem ele, imprime

ZipList {getZipList = []}

ZipListé um dos poucos tipos nas bibliotecas base com uma instância para, Applicativemas não para Monad. Pode haver alternativas mais curtas à espreita em algum lugar.

Zgarb
fonte
7

Rigoroso, 87 84 82 bytes

-5 bytes graças ao dfeuer !

Pode ser menos com o BlockArgumentssalvamento dos parênteses \_->print 1:

import Control.Exception
0!_=0
main=catch @ErrorCall(print$0!error"")(\_->print 1)

A execução com -XStrict imprime um, 1enquanto a execução com -XNoStrict imprime a 0. Isso usa que Haskell, por padrão, é preguiçoso e não precisa avaliar, error""pois já sabe que o resultado será 0quando corresponder ao primeiro argumento de (!), esse comportamento pode ser alterado com esse sinalizador - forçando o tempo de execução a avaliar os dois argumentos.

Se não for permitido imprimir nada em um caso, podemos reduzi -lo a 75 bytes, substituindo o principal por (também alguns bytes desativados pelo dfeuer ):

main=catch @ErrorCall(print$0!error"")mempty

StrictData, 106 99 93 bytes

-15 bytes graças ao dfeuer !

Isso basicamente faz o mesmo, mas funciona com campos de dados:

import Control.Exception
data D=D()
main=catch @ErrorCall(p$seq(D$error"")0)(\_->p 1);p=print

Imprime 1com o sinalizador -XStrictData e 0com -XNoStrictData .

Se nada for permitido em um caso, podemos reduzi -lo para 86 bytes, substituindo o principal por (19 bytes desativados pelo dfeuer ):

main=catch @ErrorCall(print$seq(D$error"")0)mempty

Nota: Todas as soluções requerem TypeApplicationsconjunto.

ბიმო
fonte
Você pode reduzir isso facilmente para 98 bytes, o que coincide exatamente com a minha solução (muito diferente). TIO .
dfeuer 7/03
Na verdade, você pode fazer ainda melhor: em vez de imprimir no manipulador de exceções, basta usar pure().
dfeuer 7/03
11
@fefe: Bom, o D{}truque é bem legal! Raspou outro usando em PartialTypeSignaturesvez de ScopedTypeVariables:)
8/03
11
@fefe: dei uma olhada e tentei algumas coisas, mas nunca usei Generics, então provavelmente não sou a pessoa certa.
ბიმო 8/03
11
You can do even better with bleeding edge GHC and -XBlockArguments: main=catch @ErrorCall(p$seq(D$error"")1)\_->p 3
dfeuer
6

ApplicativeDo, 146 bytes

newtype C a=C{u::Int}
instance Functor C where fmap _ _=C 1
instance Applicative C
instance Monad C where _>>=_=C 0
main=print$u$do{_<-C 0;pure 1}

Prints 1 when ApplicativeDo is enabled, 0 otherwise

Try it online!

oisdk
fonte
1
Thanks! Ah, I think I'm on an older version of GHC (the "no applicative" was a warning on my system)
oisdk
3
Using -XDeriveAnyClass you can derive Applicative and Show to save using record syntax, see this.
ბიმო
6

BinaryLiterals, 31 24 bytes

Edit:

  • -7 bytes: H.PWiz suggested adjusting it further by using a single b12 variable.

An adjustment to H.PWiz's method, avoiding the function instance.

b12=1
main=print$(+)0b12
Ørjan Johansen
fonte
6

ExtendedDefaultRules, 54 53 bytes

instance Num()
main=print(toEnum 0::Num a=>Enum a=>a)

Prints () with -XExtendedDefaultRules and 0 with -XNoExtendedDefaultRules.

This flag is enabled by default in GHCi, but not in GHC, which recently caused some confusion for me, though BMO was quickly able to help.

The above code is a golfed version of an example in the GHC User Guide where type defaulting in GHCi is explained.

-1 byte thanks to Ørjan Johansen!

Laikoni
fonte
While looking at this code borrowed into the polyglot (where the parentheses give some trouble), I remembered that GHC supports the one byte shorter syntax toEnum 0::Num a=>Enum a=>a.
Ørjan Johansen
You can get it down to 48 bytes with PartialTypeSignatures: main=print(toEnum 0::_=>Num a=>a). Also, your TIO link is out of date.
dfeuer
6

RebindableSyntax, 25 bytes

I was reading the recently posted Guide to GHC's Extensions when I noticed an easy one that I didn't recall seeing here yet.

main|negate<-id=print$ -1

Also requires -XImplicitPrelude, or alternatively import Prelude in the code itself.

  • -XRebindableSyntax changes the behavior of some of Haskell's syntactic sugar to make it possible to redefine it.
  • -1 is syntactic sugar for negate 1.
  • Normally this negate is Prelude.negate, but with the extension it's "whichever negate is in scope at the point of use", which is defined as id.
  • Because the extension is meant to be used to make replacements for the Prelude module, it automatically disables the usual implicit import of that, but other Prelude functions (like print) are needed here, so it is re-enabled with -XImplicitPrelude.
Ørjan Johansen
fonte
6

Strict, 52 bytes

import GHC.IO
f _=print()
main=f$unsafePerformIO$f()

-XStrict

-XNoStrict

With -XStrict, prints () an extra time.

Thanks to @Sriotchilism O'Zaic for two bytes.

dfeuer
fonte
6

StrictData, 58 bytes

import GHC.Exts
data D=D Int
main=print$unsafeCoerce#D 3+0

(Links are slightly outdated; will fix.)

-XNoStrictData

-XStrictData

Requires MagicHash (to let us import GHC.Exts instead of Unsafe.Coerce) and -O (absolutely required, to enable unpacking of small strict fields).

With -XStrictData, prints 3. Otherwise, prints the integer value of the (probably tagged) pointer to the pre-allocated copy of 3::Integer, which can't possibly be 3.

Explanation

It will be a bit easier to understand with a little expansion, based on type defaulting. With signatures, we can drop the addition.

main=print
  (unsafeCoerce# D (3::Integer)
    :: Integer)

Equivalently,

main=print
  (unsafeCoerce# $
    D (unsafeCoerce# (3::Integer))
    :: Integer)

Why does it ever print 3? This seems surprising! Well, small Integer values are represented very much like Ints, which (with strict data) are represented just like Ds. We end up ignoring the tag indicating whether the integer is small or large positive/negative.

Why can't it print 3 without the extension? Leaving aside any memory layout reasons, a data pointer with low bits (2 lowest for 32-bit, 3 lowest for 64-bit) of 3 must represent a value built from the third constructor. In this case, that would require a negative integer.

dfeuer
fonte
5

UnboxedTuples, 52 bytes

import Language.Haskell.TH
main=runQ[|(##)|]>>=print

Requires -XTemplateHaskell. Prints ConE GHC.Prim.(##) with -XUnboxedTuples and UnboundVarE ## with -XNoUnboxedTuples.

Laikoni
fonte
Shouldn't there be another +16 in the score for the required option -XTemplateHaskell?
celtschk
2
@celtschk I did not count it because the current meta consensus on command line flags says they are not counted but constitute a new language instead. Though upon thinking about it I see that in the context of this challenge which only allows Haskell answers but also the use of other flags it is not quite clear what todo. I'll ask OP about it.
Laikoni
I wasn't aware that the consensus on this has changed. Thank you for the pointer. Asking the OP is a good idea for sure.
celtschk
5

OverloadedLists, 76 bytes

import GHC.Exts
instance IsList[()]where fromList=(():)
main=print([]::[()])

With -XOverloadedLists it prints [()]. With -XNoOverloadedLists it prints []

This requires the additional flags: -XFlexibleInstances, -XIncoherentInstances

H.PWiz
fonte
You can get away with overlapping instances.
dfeuer
5

HexFloatLiterals, 49 25 bytes

-24 bytes thanks to Ørjan Johansen.

main|(.)<-seq=print$0x0.0

Prints 0.0 with -XHexFloatLiterals and 0 with -XNoHexFloatLiterals.

There are no TIO links because HexFloatLiterals was added in ghc 8.4.1, but TIO has ghc 8.2.2.

stasoid
fonte
main|(.)<-seq=print$0x0.0 avoids the import hiding.
Ørjan Johansen
main|let _._=0=print$0x0.0 might be easier for the polyglot though.
Ørjan Johansen
5

ScopedTypeVariables, 37 bytes

main=print(1::_=>a):: a.a~Float=>_

This also requires UnicodeSyntax,PartialTypeSignatures, GADTs, and ExplicitForAll.

Try it online (without extension)

Try it online (with extension)

Explanation

The partial type signatures are just to save bytes. We can fill them in like so:

main=print(1::(Num a, Show a)=>a):: a.a~Float=>IO ()

With scoped type variables, the a in the type of 1 is constrained to be the a in the type of main, which itself is constrained to be Float. Without scoped type variables, 1 defaults to type Integer. Since Float and Integer values are shown differently, we can distinguish them.

Thanks to @ØrjanJohansen for a whopping 19 bytes! He realized that it was much better to take advantage of the difference between Show instances of different numerical types than differences in their arithmetic. He also realized that it was okay to leave the type of main "syntactically ambiguous" because the constraint actually disambiguates it. Getting rid of the local function also freed me up to remove the type signature for main (shifting it to the RHS) to save five more bytes.

dfeuer
fonte
45 bytes
Ørjan Johansen
@ØrjanJohansen, nice.
dfeuer
@ØrjanJohansen, should I make the edit, or would you prefer to add your own?
dfeuer
Edit, it was a gradual evolution from yours.
Ørjan Johansen
@ØrjanJohansen, thanks, that was beautiful.
dfeuer
5

DeriveAnyClass, 121 113 bytes

Thanks to dfeuer for quite some bytes!

import Control.Exception
newtype M=M Int deriving(Show,Num)
main=handle h$print(0::M);h(_::SomeException)=print 1

-XDeriveAnyClass prints 1 whereas -XNoDeriveAnyClass prints M 0.

This is exploiting the fact that DeriveAnyClass is the default strategy when both DeriveAnyClass and GeneralizedNewtypeDeriving are enabled, as you can see from the warnings. This flag will happily generate empty implementations for all methods but GeneralizedNewtypeDeriving is actually smart enough to use the underlying type's implementation and since Int is a Num it won't fail in this case.


If printing nothing in case the flag is enabled replacing the main by the following would be 109 bytes:

main=print(0::M)`catch`(mempty::SomeException->_)
ბიმო
fonte
At least in runhaskell, this actually prints M 1 with -XDeriveAnyClass, due to laziness...
ceased to turn counterclockwis
@ceasedtoturncounterclockwis: Yes in GHCi as well, but when compiling on TIO (and my machine) & then running it results in 1 :)
ბიმო
113 bytes
dfeuer
109 bytes
dfeuer
1
I got it down to 104 in a completely different way, so I added my own answer.
dfeuer
4

PostfixOperators, 63 bytes

import Text.Show.Functions
instance Num(a->b)
main=print(0`id`)

Try it online (without extension)

Try it online (with extension)

This is a cut-down version of a Hugs/GHC polyglot I wrote. See that post for explanation. Thanks to @ØrjanJohansen for realizing I could use id instead of a custom operator, saving four bytes.

dfeuer
fonte
id can be used instead of !.
Ørjan Johansen
@ØrjanJohansen, yes indeed! That saves a cool four bytes.
dfeuer
3

TemplateHaskell, 140 91 bytes

Just copied from mauke with small modifications. I don't know what's going on.

-49 bytes thanks to Ørjan Johansen.

import Language.Haskell.TH
instance Show(Q a)where show _=""
main=print$(pure$TupE[]::ExpQ)

Try it online!

stasoid
fonte
$(...) (no space) is template evaluation syntax when TH is enabled, and TupE[] ("empty tuple") gives (). Using Show might work well for the polyglot, although for this particular challenge I feel a bit bad about defining a value to print as an empty string...
Ørjan Johansen
2

MonomorphismRestriction, 31 29 bytes

Edit:

  • -2 bytes with an improvement by H.PWiz
f=(2^)
main=print$f$f(6::Int)

-XMonomorphismRestriction prints 0. -XNoMonomorphismRestriction prints 18446744073709551616.

  • With the restriction, the two uses of f are forced to be the same type, so the program prints 2^2^6 = 2^64 as a 64-bit Int (on 64-bit platforms), which overflows to 0.
  • Without the restriction, the program prints 2^64 as a bignum Integer.
Ørjan Johansen
fonte
1
I think f=(2^);main=print$f$f(64::Int) would save a byte. But it won't realistically terminate
H.PWiz
@H.PWiz Fortunately 64=2^6, which saves yet another byte.
Ørjan Johansen
1

ScopedTypeVariables, 119 97 bytes

Just copied from mauke with small modifications.

Currently there are two other answers for ScopedTypeVariables: 113 bytes by Csongor Kiss and 37 bytes by dfeuer. This submission is different in that it does not require other Haskell extensions.

-22 bytes thanks to Ørjan Johansen.

class(Show a,Num a)=>S a where s::a->IO();s _=print$(id::a->a)0
instance S Float
main=s(0::Float)

Try it online!

stasoid
fonte
97 bytes (although the IO()/print trick won't work in the polyglot).
Ørjan Johansen
@ØrjanJohansen I added ScopedTypeVariables, but broke ExtendedDefaultRules. How it can be fixed? I already had such error before, but I am unable to apply your explanation here. The ScopedTypeVariables code I added is this.
stasoid
I see, the codes use similar defaulting tricks, and they interfer with each other. One solution is to let the new one use a more restricted class than Num. I think class(Show a,Floating a)=>K a where{k::a->String;k=pure$ show(f pi)where f=id::a->a}; should work, conveniently using that Float and Double display pi with different precision.
Ørjan Johansen
@ØrjanJohansen Wow, it fits right in. Thank you.
stasoid