From 257e19c2c60fe6190b160c0338b8921c0f566e7a Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 10 Oct 2024 12:26:59 +0100 Subject: [PATCH 1/7] Support Anoma stdlib curry function in nockma backend --- src/Juvix/Compiler/Nockma/StdlibFunction.hs | 1 + .../Compiler/Nockma/StdlibFunction/Base.hs | 2 + .../Compiler/Nockma/Translation/FromTree.hs | 4 ++ test/Nockma/Eval/Positive.hs | 60 ++++++++++++++++++- 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Juvix/Compiler/Nockma/StdlibFunction.hs b/src/Juvix/Compiler/Nockma/StdlibFunction.hs index 4cef7f5646..427de12cfb 100644 --- a/src/Juvix/Compiler/Nockma/StdlibFunction.hs +++ b/src/Juvix/Compiler/Nockma/StdlibFunction.hs @@ -27,6 +27,7 @@ stdlibPath = \case StdlibSignDetached -> [nock| [9 23 0 1] |] StdlibVerify -> [nock| [9 4 0 1] |] StdlibLengthList -> [nock| [9 1.406 0 31] |] + StdlibCurry -> [nock| [9 4 0 31] |] -- Obtained from the urbit dojo using: -- -- => anoma !=(~(met block 3)) diff --git a/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs b/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs index 6474dcd327..1285916751 100644 --- a/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs +++ b/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs @@ -24,6 +24,7 @@ instance Pretty StdlibFunction where StdlibFoldBytes -> "fold-bytes" StdlibLengthList -> "length-list" StdlibLengthBytes -> "length-bytes" + StdlibCurry -> "curry" data StdlibFunction = StdlibDec @@ -45,6 +46,7 @@ data StdlibFunction | StdlibFoldBytes | StdlibLengthList | StdlibLengthBytes + | StdlibCurry deriving stock (Show, Lift, Eq, Bounded, Enum, Generic) instance Hashable StdlibFunction diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index 60d33c3c16..ef89e28f28 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -31,6 +31,7 @@ module Juvix.Compiler.Nockma.Translation.FromTree runCompilerWith, emptyCompilerCtx, CompilerCtx (..), + stdlibCurry, ) where @@ -1304,3 +1305,6 @@ intToUInt8 i = callStdlib StdlibMod [i, nockIntegralLiteral @Natural (2 ^ uint8S where uint8Size :: Natural uint8Size = 8 + +stdlibCurry :: (Member (Reader CompilerCtx) r) => Term Natural -> Term Natural -> Sem r (Term Natural) +stdlibCurry f arg = callStdlib StdlibCurry [f, arg] diff --git a/test/Nockma/Eval/Positive.hs b/test/Nockma/Eval/Positive.hs index 32ca070ddf..2379566f89 100644 --- a/test/Nockma/Eval/Positive.hs +++ b/test/Nockma/Eval/Positive.hs @@ -159,6 +159,52 @@ testWithStorage s = Test defaultEvalOptions Nothing (Storage (HashMap.fromList ( test :: Text -> Term Natural -> Term Natural -> Check () -> Test test = testWithStorage [] +--- A Nock formula that computes logical and +nockAnd :: Term Natural +nockAnd = [nock| [6 [0 12] [6 [0 13] [1 0] [1 1]] [1 1]] |] + +--- A nock function that computes logical and +nockAndArgs :: Term Natural +nockAndArgs = nockAnd # args # env + where + arg :: Term Natural + arg = nockNilTagged "placeholder argument" + + args :: Term Natural + args = arg # arg + + env :: Term Natural + env = nockNilTagged "environment" + +-- | Wrap a function in a formula that calls the function with arguments from the subject. +applyFun :: Term Natural -> Term Natural +applyFun f = + OpPush + # f + # ( OpCall + # codePath + # ( OpReplace + # ( argsPath + # (OpAddress # subjectPath >># (OpAddress # argsPath)) + ) + # (OpAddress # fPath) + ) + ) + where + codePath :: Path + codePath = closurePath FunCode + + argsPath :: Path + argsPath = closurePath ArgsTuple + + -- Path to the function being applied after pushing + fPath :: Path + fPath = [L] + + -- Path to the original subject after pushing + subjectPath :: Path + subjectPath = [R] + anomaCallingConventionTests :: [Test] anomaCallingConventionTests = [True, False] @@ -172,7 +218,15 @@ anomaCallingConventionTests = in run . runReader fx . runReader emptyCompilerCtx $ do p0 <- pathToArg 0 p1 <- pathToArg 1 - return (anomaTestM "stdlib sub args" (sub (OpAddress # p0) (OpAddress # p1)) args (eqNock [nock| 2 |])) + return (anomaTestM "stdlib sub args" (sub (OpAddress # p0) (OpAddress # p1)) args (eqNock [nock| 2 |])), + --- sanity check nockAnd + anomaTestM "(and true false) == false" (return nockAnd) [nockTrueLiteral, nockFalseLiteral] (eqNock [nock| false |]), + anomaTestM "(and true true) == true" (return nockAnd) [nockTrueLiteral, nockTrueLiteral] (eqNock [nock| true |]), + --- test curry with and + anomaTestM "(curry and true) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockTrueLiteral) [nockFalseLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and true) true == true" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockTrueLiteral) [nockTrueLiteral] (eqNock [nock| true |]), + anomaTestM "(curry and false) true == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockFalseLiteral) [nockTrueLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and false) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockFalseLiteral) [nockFalseLiteral] (eqNock [nock| false |]) ] serializationTests :: [Test] @@ -232,6 +286,10 @@ serializationTests = [nock| [[0 1] [2 3] [4 5] [6 7] [8 9] [10 11] [12 13] [14 15] [16 17] [18 19] [20 21] 0] |] [nock| 308.947.677.754.874.070.959.300.747.182.056.036.528.545.493.781.368.831.595.479.491.505.523.344.414.501 |] +-- Call a formula with specified arguments +nockCall :: Term Natural -> NonEmpty (Term Natural) -> Term Natural +nockCall formula args = (OpReplace # ([R, L] # foldTerms args) # (OpQuote # formula)) >># (OpCall # [L] # (OpAddress # emptyPath)) + juvixCallingConventionTests :: [Test] juvixCallingConventionTests = [True, False] From 7e1b87914e51eb0344b4711dd1bfe45428339fdc Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 10 Oct 2024 12:27:33 +0100 Subject: [PATCH 2/7] Nock argument tuple placeholders should have length equal to arity Without this the stdlib curry function will not work --- .../Compiler/Nockma/Translation/FromTree.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index ef89e28f28..32217cbaf0 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -759,7 +759,7 @@ compile = \case args <- mapM compile _nodeAllocClosureArgs return . makeClosure $ \case FunCode -> opAddress "allocClosureFunPath" (base <> fpath <> closurePath FunCode) - ArgsTuple -> OpQuote # argsTuplePlaceholder "goAllocClosure" + ArgsTuple -> OpQuote # argsTuplePlaceholder "goAllocClosure" farity FunctionsLibrary -> OpQuote # functionsLibraryPlaceHolder StandardLibrary -> OpQuote # stdlibPlaceHolder ClosureTotalArgsNum -> nockNatLiteral farity @@ -785,8 +785,8 @@ compile = \case opAddress' :: Term Natural -> Term Natural opAddress' x = evaluated $ (opQuote "opAddress'" OpAddress) # x -argsTuplePlaceholder :: Text -> Term Natural -argsTuplePlaceholder txt = nockNilTagged ("argsTuplePlaceholder-" <> txt) +argsTuplePlaceholder :: Text -> Natural -> Term Natural +argsTuplePlaceholder txt arity = ("argsTuplePlaceholder-" <> txt) @ foldTermsOrNil (replicate (fromIntegral arity) (TermAtom nockNil)) appendRights :: (Member (Reader CompilerCtx) r) => Path -> Term Natural -> Sem r (Term Natural) appendRights path n = do @@ -976,18 +976,18 @@ runCompilerWith _opts constrs moduleFuns mainFun = compiledFuns = (OpQuote # (666 :: Natural)) -- TODO we have this unused term so that indices match. Remove it and adjust as needed : ( makeLibraryFunction - <$> [(f ^. compilerFunctionName, runCompilerFunction compilerCtx f) | f <- libFuns] + <$> [(f ^. compilerFunctionName, f ^. compilerFunctionArity, runCompilerFunction compilerCtx f) | f <- libFuns] ) - makeLibraryFunction :: (Text, Term Natural) -> Term Natural - makeLibraryFunction (funName, c) = + makeLibraryFunction :: (Text, Natural, Term Natural) -> Term Natural + makeLibraryFunction (funName, funArity, c) = ("def-" <> funName) @ makeClosure ( \p -> let nockNilHere = nockNilTagged ("makeLibraryFunction-" <> show p) in case p of FunCode -> ("funCode-" <> funName) @ c - ArgsTuple -> ("argsTuple-" <> funName) @ argsTuplePlaceholder "libraryFunction" + ArgsTuple -> ("argsTuple-" <> funName) @ argsTuplePlaceholder "libraryFunction" funArity FunctionsLibrary -> ("functionsLibrary-" <> funName) @ functionsLibraryPlaceHolder StandardLibrary -> ("stdlib-" <> funName) @ stdlibPlaceHolder ClosureTotalArgsNum -> ("closureTotalArgsNum-" <> funName) @ nockNilHere @@ -1001,7 +1001,7 @@ runCompilerWith _opts constrs moduleFuns mainFun = let nockNilHere = nockNilTagged ("makeMainFunction-" <> show p) in case p of FunCode -> run . runReader compilerCtx $ mainFunctionWrapper funcsLib c - ArgsTuple -> argsTuplePlaceholder "mainFunction" + ArgsTuple -> argsTuplePlaceholder "mainFunction" (mainFun ^. compilerFunctionArity) FunctionsLibrary -> functionsLibraryPlaceHolder StandardLibrary -> stdlib ClosureTotalArgsNum -> nockNilHere From 91cfeb7aed459631fedc7bfb4ceb99be5b79f942 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 10 Oct 2024 13:56:00 +0100 Subject: [PATCH 3/7] Use the raw call when intercepting curry stdlib call The nockma curry function is small. There's no performance benefit in implementing it directly in the evaluator --- src/Juvix/Compiler/Nockma/Evaluator.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Juvix/Compiler/Nockma/Evaluator.hs b/src/Juvix/Compiler/Nockma/Evaluator.hs index 59b515320d..881453de0e 100644 --- a/src/Juvix/Compiler/Nockma/Evaluator.hs +++ b/src/Juvix/Compiler/Nockma/Evaluator.hs @@ -197,15 +197,16 @@ evalProfile inistack initerm = ParsedOperatorCell o -> goOperatorCell o ParsedStdlibCallCell o -> do intercept' <- asks (^. evalInterceptStdlibCalls) + let nonInterceptCall = goOperatorCell (o ^. stdlibCallRaw) if - | intercept' -> goStdlibCall (o ^. stdlibCallCell) - | otherwise -> goOperatorCell (o ^. stdlibCallRaw) + | intercept' -> goStdlibCall nonInterceptCall (o ^. stdlibCallCell) + | otherwise -> nonInterceptCall where loc :: Maybe Interval loc = term ^. termLoc - goStdlibCall :: StdlibCall a -> Sem r (Term a) - goStdlibCall StdlibCall {..} = do + goStdlibCall :: Sem r (Term a) -> StdlibCall a -> Sem r (Term a) + goStdlibCall nonInterceptCall StdlibCall {..} = do let w = EvalCrumbStdlibCallArgs (CrumbStdlibCallArgs _stdlibCallFunction) args' <- withCrumb w (recEval stack _stdlibCallArgs) let binArith :: (a -> a -> a) -> Sem r (Term a) @@ -262,6 +263,7 @@ evalProfile inistack initerm = StdlibLengthBytes -> case args' of TermAtom a -> TermAtom <$> goLengthBytes a _ -> error "expected an atom" + StdlibCurry -> nonInterceptCall where goCat :: Atom a -> Atom a -> Sem r (Term a) goCat arg1 arg2 = TermAtom . setAtomHint AtomHintString <$> atomConcatenateBytes arg1 arg2 From ad143291c53c34f272306d7b19a533089ff6a868 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 10 Oct 2024 15:57:44 +0100 Subject: [PATCH 4/7] Fix compiler warning --- src/Juvix/Compiler/Nockma/Translation/FromTree.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index 32217cbaf0..21786cd5d5 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -786,7 +786,10 @@ opAddress' :: Term Natural -> Term Natural opAddress' x = evaluated $ (opQuote "opAddress'" OpAddress) # x argsTuplePlaceholder :: Text -> Natural -> Term Natural -argsTuplePlaceholder txt arity = ("argsTuplePlaceholder-" <> txt) @ foldTermsOrNil (replicate (fromIntegral arity) (TermAtom nockNil)) +argsTuplePlaceholder txt arity = ("argsTuplePlaceholder-" <> txt) @ foldTermsOrNil (replicate arityInt (TermAtom nockNil)) + where + arityInt :: Int + arityInt = fromIntegral arity appendRights :: (Member (Reader CompilerCtx) r) => Path -> Term Natural -> Sem r (Term Natural) appendRights path n = do From 21d43aedc4a8227ca638a336fc93d4e0009cba6a Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Thu, 10 Oct 2024 17:25:01 +0100 Subject: [PATCH 5/7] Use nockma AST in tests --- .../Compiler/Nockma/Translation/FromTree.hs | 2 ++ test/Nockma/Eval/Positive.hs | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index 21786cd5d5..2fea37e6a5 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -32,6 +32,8 @@ module Juvix.Compiler.Nockma.Translation.FromTree emptyCompilerCtx, CompilerCtx (..), stdlibCurry, + IndexTupleArgs (..), + indexTuple, ) where diff --git a/test/Nockma/Eval/Positive.hs b/test/Nockma/Eval/Positive.hs index 2379566f89..4020f940d0 100644 --- a/test/Nockma/Eval/Positive.hs +++ b/test/Nockma/Eval/Positive.hs @@ -161,11 +161,31 @@ test = testWithStorage [] --- A Nock formula that computes logical and nockAnd :: Term Natural -nockAnd = [nock| [6 [0 12] [6 [0 13] [1 0] [1 1]] [1 1]] |] +nockAnd = + OpIf + # (OpAddress # argPath 0) + # ( OpIf + # (OpAddress # argPath 1) + # nockTrueLiteral + # nockFalseLiteral + ) + # nockFalseLiteral + where + argPath :: Natural -> Path + argPath idx = + closurePath ArgsTuple + ++ indexTuple + ( IndexTupleArgs + { _indexTupleArgsIndex = idx, + _indexTupleArgsLength = funArity + } + ) + funArity :: Natural + funArity = 2 --- A nock function that computes logical and -nockAndArgs :: Term Natural -nockAndArgs = nockAnd # args # env +nockAndFun :: Term Natural +nockAndFun = nockAnd # args # env where arg :: Term Natural arg = nockNilTagged "placeholder argument" @@ -223,10 +243,10 @@ anomaCallingConventionTests = anomaTestM "(and true false) == false" (return nockAnd) [nockTrueLiteral, nockFalseLiteral] (eqNock [nock| false |]), anomaTestM "(and true true) == true" (return nockAnd) [nockTrueLiteral, nockTrueLiteral] (eqNock [nock| true |]), --- test curry with and - anomaTestM "(curry and true) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockTrueLiteral) [nockFalseLiteral] (eqNock [nock| false |]), - anomaTestM "(curry and true) true == true" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockTrueLiteral) [nockTrueLiteral] (eqNock [nock| true |]), - anomaTestM "(curry and false) true == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockFalseLiteral) [nockTrueLiteral] (eqNock [nock| false |]), - anomaTestM "(curry and false) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndArgs) nockFalseLiteral) [nockFalseLiteral] (eqNock [nock| false |]) + anomaTestM "(curry and true) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockTrueLiteral) [nockFalseLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and true) true == true" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockTrueLiteral) [nockTrueLiteral] (eqNock [nock| true |]), + anomaTestM "(curry and false) true == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockFalseLiteral) [nockTrueLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and false) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockFalseLiteral) [nockFalseLiteral] (eqNock [nock| false |]) ] serializationTests :: [Test] From 8f2ea58dfcf3454084180b6cf14ad9dee1d1c303 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Fri, 11 Oct 2024 11:40:19 +0100 Subject: [PATCH 6/7] Add comments describing the purpose of nonInterceptCall --- src/Juvix/Compiler/Nockma/Evaluator.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Juvix/Compiler/Nockma/Evaluator.hs b/src/Juvix/Compiler/Nockma/Evaluator.hs index 881453de0e..d7a7880e6b 100644 --- a/src/Juvix/Compiler/Nockma/Evaluator.hs +++ b/src/Juvix/Compiler/Nockma/Evaluator.hs @@ -198,6 +198,8 @@ evalProfile inistack initerm = ParsedStdlibCallCell o -> do intercept' <- asks (^. evalInterceptStdlibCalls) let nonInterceptCall = goOperatorCell (o ^. stdlibCallRaw) + -- Pass the raw call to goStdlibCall so that stdlib intercepts + -- can choose to use the raw call instead. if | intercept' -> goStdlibCall nonInterceptCall (o ^. stdlibCallCell) | otherwise -> nonInterceptCall @@ -263,6 +265,8 @@ evalProfile inistack initerm = StdlibLengthBytes -> case args' of TermAtom a -> TermAtom <$> goLengthBytes a _ -> error "expected an atom" + -- Use the raw nock code for curry. The nock stdlib curry function is + -- small. There's no benefit in implementing it separately in the evaluator. StdlibCurry -> nonInterceptCall where goCat :: Atom a -> Atom a -> Sem r (Term a) From 017a943165a25f51ecac075efe7d98b8df68bd06 Mon Sep 17 00:00:00 2001 From: Lukasz Czajka Date: Fri, 11 Oct 2024 16:04:34 +0200 Subject: [PATCH 7/7] and3 test --- test/Nockma/Eval/Positive.hs | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/test/Nockma/Eval/Positive.hs b/test/Nockma/Eval/Positive.hs index 4020f940d0..7c4f1eeb02 100644 --- a/test/Nockma/Eval/Positive.hs +++ b/test/Nockma/Eval/Positive.hs @@ -196,6 +196,47 @@ nockAndFun = nockAnd # args # env env :: Term Natural env = nockNilTagged "environment" +--- A Nock formula that computes logical and of 3 arguments +nockAnd3 :: Term Natural +nockAnd3 = + OpIf + # (OpAddress # argPath 0) + # ( OpIf + # (OpAddress # argPath 1) + # ( OpIf + # (OpAddress # argPath 2) + # nockTrueLiteral + # nockFalseLiteral + ) + # nockFalseLiteral + ) + # nockFalseLiteral + where + argPath :: Natural -> Path + argPath idx = + closurePath ArgsTuple + ++ indexTuple + ( IndexTupleArgs + { _indexTupleArgsIndex = idx, + _indexTupleArgsLength = funArity + } + ) + funArity :: Natural + funArity = 3 + +--- A nock function that computes logical and for 3 arguments +nockAnd3Fun :: Term Natural +nockAnd3Fun = nockAnd3 # args # env + where + arg :: Term Natural + arg = nockNilTagged "placeholder argument" + + args :: Term Natural + args = arg # arg # arg + + env :: Term Natural + env = nockNilTagged "environment" + -- | Wrap a function in a formula that calls the function with arguments from the subject. applyFun :: Term Natural -> Term Natural applyFun f = @@ -246,7 +287,16 @@ anomaCallingConventionTests = anomaTestM "(curry and true) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockTrueLiteral) [nockFalseLiteral] (eqNock [nock| false |]), anomaTestM "(curry and true) true == true" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockTrueLiteral) [nockTrueLiteral] (eqNock [nock| true |]), anomaTestM "(curry and false) true == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockFalseLiteral) [nockTrueLiteral] (eqNock [nock| false |]), - anomaTestM "(curry and false) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockFalseLiteral) [nockFalseLiteral] (eqNock [nock| false |]) + anomaTestM "(curry and false) false == false" (applyFun <$> stdlibCurry (OpQuote # nockAndFun) nockFalseLiteral) [nockFalseLiteral] (eqNock [nock| false |]), + --- sanity check nockAnd3 + anomaTestM "(and3 true false true) == false" (return nockAnd3) [nockTrueLiteral, nockFalseLiteral, nockTrueLiteral] (eqNock [nock| false |]), + anomaTestM "(and3 true true false) == false" (return nockAnd3) [nockTrueLiteral, nockTrueLiteral, nockFalseLiteral] (eqNock [nock| false |]), + anomaTestM "(and3 true true true) == true" (return nockAnd3) [nockTrueLiteral, nockTrueLiteral, nockTrueLiteral] (eqNock [nock| true |]), + --- test curry with and3 + anomaTestM "(curry and3 true) false true == false" (applyFun <$> stdlibCurry (OpQuote # nockAnd3Fun) nockTrueLiteral) [nockFalseLiteral, nockTrueLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and3 true) true true == true" (applyFun <$> stdlibCurry (OpQuote # nockAnd3Fun) nockTrueLiteral) [nockTrueLiteral, nockTrueLiteral] (eqNock [nock| true |]), + anomaTestM "(curry and3 false) true true == false" (applyFun <$> stdlibCurry (OpQuote # nockAnd3Fun) nockFalseLiteral) [nockTrueLiteral, nockTrueLiteral] (eqNock [nock| false |]), + anomaTestM "(curry and3 false) false true == false" (applyFun <$> stdlibCurry (OpQuote # nockAnd3Fun) nockFalseLiteral) [nockFalseLiteral, nockTrueLiteral] (eqNock [nock| false |]) ] serializationTests :: [Test]