等等,这是什么语言?


37

最近,我很高兴编写了一个Haskell程序,该程序可以检测到NegativeLiterals扩展是否被使用。我想出了以下几点:

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

在线尝试!

它将True正常打印,False否则打印。

现在,我的工作非常有趣,我将挑战扩展到了所有人。您还可以破解其他哪些Haskell语言扩展?

规则

要破解特定的语言扩展,您必须编写一个Haskell程序,该程序可以同时编译有无语言扩展(警告很好),并在使用该语言扩展运行并关闭时输出两个不同的非错误值(通过将No前缀添加到语言扩展)。这样,上面的代码可以简化为:

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

打印1-1

您用来破解扩展程序的任何方法都必须特定于该扩展程序。如果不允许的话,可能有一些方法可以任意检测启用了哪些编译器标志或LanguageExtensions。您可以启用其他语言扩展或更改编译器优化-O,而无需花费任何字节数。

语言扩展

你无法破解任何语言扩展,没有一个No对应物(例如Haskell98Haskell2010UnsafeTrustworthySafe),因为这些不属于上文所列的条款。所有其他语言扩展都是公平的游戏。

计分

您是第一个破解的人,每种语言扩展都会获得一分,而您破解最短(以字节为单位)的每种语言扩展将获得一分。对于第二点,将打破联系以支持较早的提交。分数越高越好

您将无法为首次提交打分,NegativeLiterals或者QuasiQuotes因为我已经破解了它们并将其包含在帖子正文中。但是,您将能够在每一个裂缝中得分最短的地方得分。这是我的裂缝QuasiQuotes

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

在线尝试!


3
我认为是所有有效选项的列表
H.PWiz

1
请注意,NondecreasingIndentation由于明显的原因,我的上述评论未包括在内
-H.PWiz

4
我认为该标题具有误导性,因为您可以使用的唯一语言是Haskell。怎么样,Wait, what language extension is this?或者完全不同的东西。
MD XF

1
我很好奇是否有可能破解RelaxedPolyRec,因为一个足够古老的编译器实际上可以支持将其关闭。(该选项在停止执行任何操作后,随文档一起徘徊了数年。)
dfeuer

1
@dfeuer看着这张票,似乎GHC 6.12.1支持关闭它。
与Orjan约翰森

Answers:


24

MagicHash,30个字节

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

-XMagicHash输出1 -XNoMagicHash输出2

MagicHash允许变量名以结尾#。因此,通过扩展,它定义了两个函数y#x#并且每个函数都有一个值并返回一个常量21x#x将返回1(因为它x#应用于1

没有扩展名,它定义了一个函数,该函数#需要两个参数并返回2。这x#a=1是永远不会达到的模式。然后x#x1#1,返回2。


2
我现在在唱歌X Magic Hash舞动魔术之舞。希望您感到骄傲!
TRiG

令我惊讶的是,MagicHash它不允许使用非拖尾式哈希。奇怪的!
dfeuer

18

CPP,33 20字节

main=print$0-- \
 +1

0-XCPP1用打印-XNoCPP

使用-XCPP\在换行符之前加一个斜杠可删除换行符,因此该代码成为main=print$0-- +1且仅在注释的一部分时0被打印+1

如果没有标志,则注释将被忽略,并且第二行被缩进,因此被解析为前一行的一部分。


以前的方法 #define

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

也打印0-XCPP1-XNoCPP


2
哦,天哪,直到现在,我还以为GHC会在转交给CPP之前先删除Haskell的评论。
立方

@Cubic它不是处理器吗?
Bergi

1
@Bergi当然可以,但是预处理程序并不一定意味着“是运行的第一件事”,特别是因为GHC必须首先传递文件才能找到杂项。我想评论会保留在CPP完成后的文档评论等工作中。
立方


14

BinaryLiterals,57个字节

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

-XBinaryLiterals打印单个换行符。-XNoBinaryLiterals打印一个1

我相信有更好的方法可以做到这一点。如果找到一个,请发布。


您不能仅将其定义b为一个函数(这样b(0, 1),二进制不会变为,而二进制变为0b1)?
NoOneIsHere18年

12

MonomorphismRestriction + 7个其他,107字节

这使用TH,它-XTemplateHaskell始终需要标志。

文件T.hs,81 + 4字节

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

主22字节

import T
main=print $t

使用标志MonomorphismRestriction进行编译会强制使用pto 的类型,Integer -> Integer -> Integer从而产生以下输出:

"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"

使用标志NoMonomorphismRestriction进行编译时,将保留p最通用的类型,即。Num a => a->a->a-产生类似(将VarT名称简称为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"

在线尝试!


备择方案

由于上述代码只是打印出的类型p,因此可以使用所有以某种方式影响Haskell推断类型的标志来完成此操作。我将只指定该标志及其替换功能,p并在需要时指定其他标志(除外-XTemplateHaskell):

重载列表,106字节

其他需求-XNoMonomorphismRestriction

p=[]

无论是p :: [a]p :: IsList l => l尝试到网上!

OverloadedStrings,106个字节

其他需求-XNoMonomorphismRestriction

p=""

无论是p :: Stringp :: IsString s => s尝试到网上!

PolyKinds,112个字节

这完全是由于@CsongorKiss:

data P a=P 

无论是P :: P aP :: forall k (a :: k). P a尝试到网上!

MonadComprehensions,114个字节

p x=[i|i<-x]

无论是p :: [a] -> [a]p :: Monad m => m a -> m a尝试到网上!

NamedWildCards,114个字节

这是@Laikoni发现的,它还需要-XPartialTypeSignatures

p=id::_a->_a

它们都具有保存类型(p :: a -> a),但是GHC会为变量生成不同的名称,请在线尝试!

适用,120字节

p x=do i<-x;pure i

无论是p :: Monad m => m a -> m ap :: Functor f => f a -> f a尝试到网上!

重载标签,120字节

这需要附加标志-XFlexibleContexts

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

输入as p :: a -> b -> bp :: IsLabel "id" (a->b) => a -> b,请在线尝试!


类似的事情对其他标志有用吗?
H.PWiz '18年

是的,您可以这样做,OverloadedStrings或者OverloadedLists可以肯定也可以这样做
。-

2
它也适用于PolyKinds在线尝试!
Csongor Kiss

1
似乎也可以使用NamedWildCards在线尝试!(要求-XPartialTypeSignatures
Laikoni

10

CPP,27 25

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

在线尝试!

打印()-XCPP1用于-XNoCPP

先前版本:

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

在线尝试!

[1]使用-XCPP和进行打印[1,2]

鸣谢:这是受Laikoni的答案启发的,但#define它不是A 而是仅使用C注释。


9

ScopedTypeVariables,162个 113字节

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

-XScopedTypeVariables打印""(空),- XNoScopedTypeVariables打印"[()]"

编辑:由于评论中的有用建议,更新了解决方案


1
啊,我明白了。通常,将代码包含在主体中会更好,但是非高尔夫版本也很不错。我还注意到"T"可以将替换为""
小麦巫师

2
您可以做的另一件事是将数据类型替换T()。避免必须定义它。在线尝试!
小麦巫师

1
不错,我只是意识到可以将不连贯的实用程序作为标记包括在内:在线尝试!
Csongor Kiss

2
此外,show 可以更改其打印
格式

的Unicode语法forall将为您节省一些字节。我怀疑,任何需要额外实例的解决方案都有很大的获胜希望。
dfeuer

9

MonoLocalBinds,GADT或TypeFamilies,36 32字节

编辑:

  • -4个字节:stasoid 将其版本合并到了伟大的多语系链中,他通过将所有声明放在顶层使我感到惊讶。显然,触发此限制不需要实际的本地绑定。
a=0
f b=b^a
main=print(f pi,f 0)
  • 没有扩展名,这个程序将打印(1.0,1)
  • 使用标志-XMonoLocalBinds-XGADTs-XTypeFamilies中的任何一个,它都会打印(1.0,1.0)

  • MonoLocalBinds扩展的存在是为了防止GADTs和类型的家庭引起了一些直观的类型推断。因此,此扩展名被其他两个自动打开。

  • 可以关掉它再次明确地-XNoMonoLocalBinds,这一招假定你不知道。
  • 像其更著名的表亲一样,单态性限制MonoLocalBinds通过防止某些值(在局部绑定中,例如letwhere,因此名称显然也可能出现在顶层)来实现多态。尽管是为更合理的类型推断而创建的,如果可能的话,触发规则比MR还要冗长。

  • 没有任何扩展名,上面的程序会推断类型f :: Num a => a -> a,允许f pi默认为a Doublef 0an Integer

  • 使用扩展名,推断的类型变为f :: Double -> Double,并且f 0还必须返回a Double
  • 独立变量a=0是需要触发技术规则:a通过单态限制命中,并且a是一个自由变量f,这意味着f的结合基团不完全广义,该装置f关闭,因此不会成为多晶型。

9

OverloadedStrings,65 48 32字节

利用RebindableSyntax,使用我们自己的fromString版本将任何字符串文字转换为"y"

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

必须使用编译-XRebindableSyntax -XImplicitPrelude

没有-XOverloadedStrings印刷品""; 与版画"y"

同样,只是现在让我吃惊的是,相同的技术适用于(例如)OverloadedLists:

重载列表,27个字节

main=print[0]
fromListN=(:)

必须使用编译-XRebindableSyntax -XImplicitPrelude

没有-XOverloadedLists印刷品[0]; 与版画[1,0]


1
您可以将最后一行缩短为fromString a=['y']
与Orjan约翰森

print "n"也可以删除其中的空间。
Laikoni '18

@ØrjanJohansen谢谢!我的测试失败了="y",但=['y']工作正常!
felixphew

1
您可以nprint"n"
Wheat Wizard

1
您也可以使用-XImplicitPreludeafter RebindableSyntax来避免导入行。
dfeuer

8

BangPatterns,32个字节

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

-XBangPatterns打印,1-XNoBangPatterns打印0

这利用了标志BangPatterns允许用来注释模式!以强制评估WHNF,在这种情况下9!1将使用顶级定义(!)=seq。如果未启用该标志,则f!_定义一个新的运算符,(!)并在顶层定义中显示阴影。


7

ApplicativeDo,104个字节

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

在线尝试!

使用ApplicativeDo,此打印

ZipList {getZipList = [1]}

没有它,它会打印

ZipList {getZipList = []}

ZipList与实例的基础库的几个类型之一Applicative,但不是Monad。可能会有更短的选择潜伏在某个地方。


7

严格,87 84 82字节

-5个字节,感谢dfeuer

BlockArguments保存周围的括号可能会更少\_->print 1

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

与运行此-XStrict打印1,而与运行它-XNoStrict将打印0。这使用Haskell默认情况下是惰性的,不需要评估,error""因为它已经知道结果将0与的第一个参数匹配时发生(!),可以使用该标志更改此行为-强制运行时评估两个参数。

如果在一种情况下不打印任何内容,我们可以将其缩减为75个字节,用main替​​换(也可以用dfeuer删除一些字节):

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

StrictData,106 99 93字节

-15个字节,感谢dfeuer

这基本上是相同的,但是可以使用数据字段:

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

打印1-XStrictData标志和0-XNoStrictData

如果在一种情况下不打印任何内容,我们可以将其缩减为86个字节,用main替​​换(dfeuer减少 19个字节):

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

注意:所有解决方案都需要TypeApplications设置。


您可以轻松地将其缩减为98个字节,这恰好与我的(非常不同的)解决方案完全匹配。TIO
dfeuer

实际上,您甚至可以做得更好:只需使用,而不是在异常处理程序中进行打印pure()
dfeuer

1
@dfeuer:不错,D{}技巧很酷!通过使用剃光另一个一次性PartialTypeSignatures的,而不是ScopedTypeVariables:)
ბიმო

1
@dfeuer:我看了一下,尝试了一些东西,但是我从未使用过Generics,所以我可能不是合适的人。
ბიმო

1
您甚至可以利用边缘GHC和-XBlockArguments以下内容main=catch @ErrorCall(p$seq(D$error"")1)\_->p 3
做得更好

6

ApplicativeDo,146个字节

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}

启用ApplicativeDo时显示1,否则显示0

在线尝试!


1
谢谢!嗯,我认为我使用的是GHC的旧版本(“无适用”是我的系统的警告)
oisdk

3
使用-XDeriveAnyClass可以导出ApplicativeShow使用记录语法进行保存,请参见this
ბიმო


6

ExtendedDefaultRules,54 53字节

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

()-XExtendedDefaultRules0用打印-XNoExtendedDefaultRules

默认情况下,GHCi中启用了此标志,而GHCi中没有启用该标志,尽管BMO很快就可以提供帮助,但最近使我感到困惑的是。

上面的代码是《 GHC用户指南》中示例的简化版本,其中解释了GHCi中的类型默认值。

-1个字节感谢ØrjanJohansen


在查看借用到多语言的代码(括号给您带来麻烦)时,我记得GHC支持短一字节的语法toEnum 0::Num a=>Enum a=>a
与Orjan约翰森

你可以把它降低到48个字节PartialTypeSignaturesmain=print(toEnum 0::_=>Num a=>a)。另外,您的TIO链接已过期。
dfeuer

6

RebindableSyntax,25个字节

我在阅读最近发布的《 GHC扩展指南》时,发现有一个我不记得的简单指南

main|negate<-id=print$ -1

也需要-XImplicitPrelude,或者import Prelude在代码本身中也需要。

  • -XRebindableSyntax 更改Haskell某些语法糖的行为,以便可以重新定义它。
  • -1是的语法糖negate 1
  • 通常negatePrelude.negate,但扩展名为“ negate使用时范围内的任何一个”,定义为id
  • 由于该扩展名是用于替换该Prelude模块的,因此它会自动禁用通常的隐式导入,但是此处需要其他Prelude功能(例如print),因此可以使用来重新启用-XImplicitPrelude

6

严格,52个字节

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

-X严格

-XNoStrict

使用-XStrict,打印()更多时间。

感谢@Sriotchilism O'Zaic提供了两个字节。


6

StrictData,58个字节

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

(链接有些过时;将修复。)

-XNoStrictData

-XStrictData

需要MagicHash(让我们GHC.Exts代替导入Unsafe.Coerce)和-O(绝对必要,以使小的严格字段解压缩)。

使用-XStrictData,将打印3。否则,将(可能是标记的)指针的整数值打印到的预分配副本3::Integer,该副本不可能是3。

说明

根据类型默认值,稍加扩展就会更容易理解。使用签名,我们可以删除添加项。

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

等效地,

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

为什么会打印3?这似乎令人惊讶!好吧,小Integer值非常像Ints 表示,而(具有严格数据)像Ds 一样表示。我们最终忽略了指示整数是小还是大正/负的标记。

为什么没有扩展名就无法打印3?撇开任何内存布局原因,低位(32位最低为2,64位最低为3)的数据指针必须表示从第三个构造函数生成的值。在这种情况下,这将需要一个整数。


5

UnboxedTuples,52个字节

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

需要-XTemplateHaskell。打印ConE GHC.Prim.(##)-XUnboxedTuplesUnboundVarE ##-XNoUnboxedTuples


所需选项的分数不应该再有+16 -XTemplateHaskell吗?
celtschk

2
@celtschk我没有计算它,因为当前关于命令行标志的元共识说,它们不计算在内,而是构成一种新语言。尽管经过思考,我发现在这种挑战的背景下,这种挑战仅允许Haskell回答,但也可以使用其他标志,不清楚要做什么。我会问OP。
Laikoni

我不知道对此达成的共识已经改变。谢谢你的指点。确定要问OP是个好主意。
celtschk

5

重载列表,76个字节

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

使用-XOverloadedLists可以打印[()]。使用-XNoOverloadedLists可以打印[]

这需要附加标志:-XFlexibleInstances-XIncoherentInstances


您可以避免重叠的实例。
dfeuer

5

HexFloatLiterals49 25字节

感谢ØrjanJohansen,使用-24个字节。

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

0.0-XHexFloatLiterals0用打印-XNoHexFloatLiterals

没有TIO链接,因为在ghc 8.4.1中添加了HexFloatLiterals,但是TIO具有ghc 8.2.2。


main|(.)<-seq=print$0x0.0避免导入隐藏。
与Orjan约翰森

main|let _._=0=print$0x0.0对于多语言可能会更容易。
与Orjan约翰森

5

ScopedTypeVariables,37个字节

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

这也要求UnicodeSyntaxPartialTypeSignaturesGADTs,和ExplicitForAll

在线尝试(不带扩展名)

在线试用(带扩展名)

说明

部分类型签名仅用于保存字节。我们可以这样填写它们:

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

对于作用域类型变量,ain的in 1被约束为ain的in main,其本身被约束为in Float。如果没有作用域类型变量,则1默认为type Integer。由于FloatInteger值的显示方式不同,因此我们可以区分它们。

感谢@ØrjanJohansen提供多达19个字节!他意识到,利用Show不同数值类型的实例之间的差异要好于其算术差异。他还意识到,可以保留main“语法上模棱两可” 的类型,因为约束实际上可以消除歧义。摆脱本地功能还使我腾出了精力,可以删除类型签名main(将其移至RHS)以节省五个字节。



@ØrjanJohansen,很好
dfeuer

@ØrjanJohansen,我应该进行编辑,还是要添加自己的编辑?
dfeuer

编辑,这是您的渐进式发展。
与Orjan约翰森

@ØrjanJohansen,谢谢,太好了。
dfeuer

5

DeriveAnyClass,121个 113字节

感谢dfeuer提供了很多字节!

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

-XDeriveAnyClass打印,1-XNoDeriveAnyClass打印M 0

这利用了以下事实:从警告中可以看到,当同时启用DeriveAnyClass和GeneralizedNewtypeDeriving时,DeriveAnyClass是默认策略。该标志将很乐意为所有方法都是空实现,但GeneralizedNewtypeDeriving实际上是足够聪明的使用基础类型的实现,因为IntNum它不会在这种情况下失败。


如果在启用该标志的情况下不打印任何内容,则用main以下内容替换109字节

main=print(0::M)`catch`(mempty::SomeException->_)

至少在runhaskell这实际打印M 1-XDeriveAnyClass,由于懒惰......
停止转counterclockwis

@ceasedtoturncounterclockwis:是在GHCI为好,但对TIO(和我的机器)编译和运行,然后当它导致1:)
ბიმო



1
我以完全不同的方式将其降至104,所以我添加了自己的答案。
dfeuer

4

PostfixOperators,63个字节

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

在线尝试(不带扩展名)

在线试用(带扩展名)

这是我写的Hugs / GHC多语言版本的简化版本。请参阅该帖子以获取解释。感谢@ØrjanJohansen的实现,我可以使用它id代替自定义运算符,从而节省了四个字节。


id可以代替!
与Orjan约翰森

@ØrjanJohansen,的确是!这样可以节省四个字节。
dfeuer




3

TemplateHaskell,140 91字节

只是从mauke复制而来,但了一些小的修改。我不知道这是怎么回事。

-49个字节,感谢ØrjanJohansen。

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

在线尝试!


$(...)(没有空格)是启用TH时的模板评估语法,并且TupE[](“空元组”)给出()使用Show可能会有不错的多语种,虽然这个特别的挑战,我觉得有关定义的值打印为空字符串有点不好...
与Orjan约翰森

2

MonomorphismRestriction,31个 29字节

编辑:

  • -2字节,由H.PWiz改进
f=(2^)
main=print$f$f(6::Int)

-XMonomorphismRestriction打印0-XNoMonomorphismRestriction打印18446744073709551616

  • 由于存在限制,必须将的两种用法f设为同一类型,因此程序2^2^6 = 2^64将以64位Int(在64位平台上)打印,并溢出到0
  • 在没有限制的情况下,程序将打印2^64为bignum Integer

1
我认为f=(2^);main=print$f$f(64::Int)可以节省一个字节。但是它实际上不会终止
H.PWiz

@ H.PWiz幸运的是64=2^6,它保存了另一个字节。
与Orjan约翰森

1

ScopedTypeVariables,119 97字节

只是从mauke复制而来,但了一些小的修改。

目前还有其他两个答案为ScopedTypeVariables:通过琼戈尔吻113个字节37个字节的dfeuer。提交内容的不同之处在于,它不需要其他Haskell扩展。

-22个字节,感谢ØrjanJohansen。

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

在线尝试!


97字节(尽管该IO()/print技巧在多语言版本中不起作用)。
与Orjan约翰森

@ØrjanJohansen我添加了ScopedTypeVariables,但破坏了ExtendedDefaultRules。如何解决?之前我已经有这样的错误,但是我无法在这里应用您的解释。我添加的ScopedTypeVariables代码是this
stasoid

我看到,这些代码使用了类似的默认技巧,并且它们相互干扰。一种解决方案是让新方案使用比更为严格的类Num。我认为class(Show a,Floating a)=>K a where{k::a->String;k=pure$ show(f pi)where f=id::a->a};应该可以正常使用它Float并以不同的精度进行Double显示pi
与Orjan约翰森

@ØrjanJohansen哇,很合适。谢谢。
stasoid
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.