Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC 101/166 style #118

Merged
merged 128 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 123 commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
d2cc629
Apply hlint suggestions
Lucus16 Apr 6, 2023
d83a00c
Generalize parsing utilities
Lucus16 Apr 6, 2023
c8ebd02
Attach leading trivia to next token
Lucus16 Apr 6, 2023
0c9d9a4
Add test for if-with-comments
Lucus16 Apr 6, 2023
f702d73
Fix argument order in Ann
Lucus16 Apr 6, 2023
5ae8e62
Allow standalone comments in lists and sets
Lucus16 Apr 29, 2023
4a05494
Add direnv
piegamesde May 5, 2023
13b7904
Update CLI flags description
piegamesde May 5, 2023
f938cfc
Add simple test runner
piegamesde May 5, 2023
9ce4ce3
Rework function declarations
piegamesde Apr 18, 2023
e05d6a2
Force-expand lists with more than one item
piegamesde Apr 18, 2023
305ed77
Expand let statements more
piegamesde Apr 18, 2023
73e7ec4
Rework if statements
piegamesde Apr 18, 2023
cd0acd3
Don't indent `in` body anymore
piegamesde May 5, 2023
8315ca3
Rework `inherit` statements
piegamesde May 5, 2023
28a54b3
fixup! Rework if statements
piegamesde May 5, 2023
7fe6a99
Add some code documentation
piegamesde May 5, 2023
e9cdac6
Rework bindings
piegamesde May 5, 2023
2e2797a
Tests: replace diff/idioms_pkgs_3
piegamesde May 7, 2023
dafec15
fixup! Rework bindings
piegamesde May 7, 2023
de7bd6d
Expand singleton lists with a multiline item
piegamesde May 7, 2023
d1ba3cd
Test: add idioms_nixos_2
piegamesde May 7, 2023
e6bed2a
Don't absort `in` body anymore
piegamesde May 17, 2023
a226b6d
Improve group handling for bindings and inherit
piegamesde May 18, 2023
0528d78
Special case selection operator
piegamesde May 18, 2023
8a6cb8c
Rework operators
piegamesde May 19, 2023
487d313
Rework parentheses
piegamesde May 19, 2023
6ce18ad
Rework function calls
piegamesde May 19, 2023
3802eaa
Add direnv
piegamesde May 5, 2023
7c18e90
Merge branch 'leading-comments' into rfc101-style
piegamesde May 19, 2023
10745f3
Fix function commas
piegamesde May 19, 2023
20d35b3
Move comments around a bit
piegamesde May 20, 2023
2a9ec6b
Tweak operations some more
piegamesde May 20, 2023
cb309e4
wip
piegamesde May 21, 2023
6f4791d
pretty parentheses
piegamesde May 21, 2023
8d07dc9
stupid silly bug
piegamesde May 21, 2023
b1b9313
Absorb parenthesized abstractions with multiple arguments
piegamesde Jun 6, 2023
0b26acb
Tweak operations some more
piegamesde Jun 6, 2023
3e16d20
Don't force-expand (simple) if statements anymore
piegamesde Jun 6, 2023
db2c938
Special case binary operators
piegamesde Jun 6, 2023
66ac4b4
Improve error message on verify
piegamesde Jun 8, 2023
5a8eb61
Improve layouting algorithm
piegamesde Jun 8, 2023
eeb2534
Factor out function application code
piegamesde Jun 8, 2023
792c405
Infinisil style function application
piegamesde Jun 9, 2023
b7daac9
Improve comment handling
piegamesde Jun 10, 2023
b86d8ea
Unindent semicolons again
piegamesde Jun 14, 2023
9a6cc7f
Don't absorb lambda body
piegamesde Jun 14, 2023
3da135d
Absorb parenthesized function calls again
piegamesde Jun 14, 2023
66b6712
Binder with with: absorb less
piegamesde Jun 22, 2023
5bb0639
Rework sets and lists
piegamesde Jun 23, 2023
f7cf76f
Force-spread inherit with more than three items
piegamesde Jun 23, 2023
a7dc8bb
Binder: Always absorb strings and paths
piegamesde Jun 23, 2023
4e1ff44
Tests: add lib/systems/parse.nix
piegamesde Jun 23, 2023
c926692
Better trailing comment parsing
piegamesde Jun 22, 2023
c2702df
Function application: fix edge case
piegamesde Jun 24, 2023
538663c
Function application: don't always absorb last argument
piegamesde Jun 24, 2023
325305e
Binders: force-expand nested attribute sets
piegamesde Jun 25, 2023
a95bd3a
Abstraction: don't absorb body when there are more than two parameters
piegamesde Jun 25, 2023
32609c8
Ignore comments in line length calculation
piegamesde Jun 25, 2023
c568bfa
Ignore indentation in line length calculation
piegamesde Jun 25, 2023
1e42aa2
Merge remote-tracking branch 'upstream/master' into rfc101-style
piegamesde Jun 26, 2023
e6693ca
Absorb abstraction in binder
piegamesde Jul 4, 2023
3b26c7d
Improve mapFirstToken code style
piegamesde Jul 8, 2023
4c0007e
Tests: Add check-meta.nix
piegamesde Jul 9, 2023
ef310bb
Improved helper functions
piegamesde Jul 17, 2023
a54a01a
Improve priority group handling
piegamesde Jul 12, 2023
c4bfa2c
Rework `//` operator
piegamesde Jul 9, 2023
81d3cf8
Copy special cases over to `++` operator
piegamesde Jul 13, 2023
cc5b426
Introduce support for optional trailing commas
piegamesde Jul 17, 2023
4468e9b
Expand attrset function parameters less
piegamesde Jul 16, 2023
9ac66a9
Rework renderer again
piegamesde Jul 18, 2023
a93dcf7
Binders: be more selective about semicolon placement
piegamesde Jul 17, 2023
43dd01d
Fix comment handling
piegamesde Sep 28, 2023
549541f
Expand singleton lists again
piegamesde Oct 10, 2023
7649a1b
Put semicolons on same line again
piegamesde Oct 11, 2023
8645a22
Update the default line length to 100
piegamesde Jun 23, 2023
d2d4a54
Tests: add make-derivation.nix
piegamesde Nov 17, 2023
8a5109b
Types: introduce mapLastToken
piegamesde Nov 17, 2023
55aae75
Function declarations: fixup commas and comments more
piegamesde Nov 17, 2023
91acfa0
Tests: add interpolation test
piegamesde Nov 29, 2023
064bf4d
String interpolations: Ignore line length limits
piegamesde Nov 17, 2023
9d7317f
String interpolations: compact function applications
piegamesde Nov 18, 2023
46d12b2
Function application: Compact simple functions
piegamesde Nov 18, 2023
62fe12f
String interpolations: Fix indentation
piegamesde Nov 18, 2023
951261e
Strings: Fix single-line strings with double single quotes
piegamesde Nov 29, 2023
136edf4
Inherit: Indent trailing semicolon again
piegamesde Nov 29, 2023
206653b
List, Attrset: Remove surrounding spaces
piegamesde Nov 29, 2023
27ce996
Fix idempotency, enable --verify in tests
piegamesde Dec 1, 2023
21ef16e
Move some more comments up
piegamesde Dec 1, 2023
fda8afa
Function application: Fix indentation with multiline function arguments
piegamesde Dec 10, 2023
0a8c246
Revert "List, Attrset: Remove surrounding spaces"
piegamesde Dec 13, 2023
35da232
Strings: Fix multi-line strings that end with a single quote
piegamesde Jan 3, 2024
a273e5a
Lists: Fix absorbtion rules
piegamesde Jan 8, 2024
070063e
Assert: always force-expand
piegamesde Jan 15, 2024
53c7361
Rework: with, paramAttr, bindings, parentheses
piegamesde Jan 15, 2024
067c281
Force-expand attrsets in attrset functions
piegamesde Jan 15, 2024
d6930fd
Refactoring
piegamesde Jan 23, 2024
08fe739
Fix false positive in --verify checks
piegamesde Feb 3, 2024
bb45962
Treat some parenthesized expressions as "simple"
piegamesde Feb 2, 2024
a75658d
Strings: Don't normalize anymore
piegamesde Feb 4, 2024
543b65f
WIP: Automatically minimize verification failures
piegamesde Feb 4, 2024
5ab9430
Relax dependency version bounds
piegamesde Sep 23, 2023
887a40d
Update Nix dependencies
piegamesde Oct 2, 2023
6cfbc2c
Fix TypeOperators warning
infinisil Oct 31, 2023
bbe0ba5
Improve multiline asserts
piegamesde Feb 6, 2024
eb732b1
Rework indentation handling
piegamesde Feb 5, 2024
62b0c39
Small cleanup
piegamesde Feb 6, 2024
d1067e4
Predoc: Support multiple priority groups
piegamesde Feb 6, 2024
7b1fcf5
Function application: Expand non-last arguments when rest fits onto a…
piegamesde Feb 6, 2024
a273a47
Add some tests
piegamesde Feb 7, 2024
9ec2ac0
If: Move trailing comments around
piegamesde Feb 24, 2024
aa85d38
Predoc: Prevent trailing whitespace on lines
piegamesde Feb 26, 2024
d531cef
With: Don't absorb when there is a trailing comment
piegamesde Feb 25, 2024
8f4bf7e
Abstraction: Don't absorb when there is a trailing comment
piegamesde Feb 26, 2024
3b95476
Binding: Small refactoring & add test
piegamesde Feb 26, 2024
bcbf8c6
Binding: Don't absorb // and ++ when there is a trailing comment
piegamesde Feb 26, 2024
d6902b0
Fix indentation
piegamesde Feb 25, 2024
6413a12
List, Attrset: Preserve empty line when there are no items
piegamesde Feb 24, 2024
6bd07b5
Fix parenthesized function application
piegamesde Feb 26, 2024
22fa00e
Parentheses: Move trailing comments up and in
piegamesde Feb 26, 2024
f7d9cfe
Application: add some special cases
piegamesde Feb 26, 2024
25792d4
Comments: use RFC style
piegamesde Mar 1, 2024
2b5ee82
String interpolation: Indentation fixes
piegamesde Mar 1, 2024
7d99e0b
Merge branch 'master' into rfc101-style
infinisil Mar 13, 2024
4eb99c1
Use /usr/bin/env shebang in tests
infinisil Mar 13, 2024
08b34c0
Comply with REUSE
infinisil Mar 13, 2024
f43ec86
Remove accidentally committed file
infinisil Mar 13, 2024
8d13b59
Parser: Fix operator parsing
piegamesde Mar 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use_nix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this added? Should it be up to the user whether or not they would want to use this specific default Nix shell? Committing this to the repo means anyone with direnv installed is going to get that big red consent message after cd-ing into the project for the first time. This also makes this hard to extend if users have other environment needs & now have to deal with potentially accidentally committing those changes as this personal environment file is now checked into the repository.

I would prefer this be moved to .envrc.example but given it is a single line & those with direnv already know how to use it, a line could be added to the README or flake that says enabling is echo "use flake" > .envrc && direnv allow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about direnv deny? Also works with direnv disallow, direnv block, and direnv revoke

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't understand your point. Committing the .envrc file is the point of direnv, to make the developer experience smoother. I love it when projects do that. The shell is additive and not pure, so it will take the outside environment into account.

Copy link
Contributor

@toastal toastal Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

direnv deny

This isn’t the experience a user wants. They want to enable direnv, but only after they have a) had time to inspect the .envrc file & b) made modifications for themselves. Current state throws up ANSI red prompt just for cd-ing into the directory. Allow should happen after everything is ready.

the point

Scenario: You and I have different setup needs. I need different environment variables & scripts specific to me. The only way to get them for my personal setup is to edit the .envrc. Now that I have edited the .envrc, git status is saying I have made modification even tho they should be specific to me. I might also accidentally commit those changes as a result since it’s a tracked file. I believe the point is to set up your environment for you, not for everyone as not everyone has the same environment. The point of an .envrc.example is the place for a suggested setup for the average user. cp .envrc.example .envrc is one tiny step. Many projects have .env in their .gitignore because they don’t want to a) check in sensitive env info b) prescribe everyone have the same setup. This is as weird as committing your personal text editor settings… they are your environment settings, not everyone else’s.

love it

I hate it. If I just need to edit a README, I don’t want a direnv prompt, & certainly don’t want to copy down another version of Nixpkgs with tooling I won’t be needing for my task, and definitely not automatically running arbritrary executables I don’t yet understand because I just cloned the repo. I do like it when they offer an example I can copy, or if there is nothing interesting but use flake or use nix, just stick it in the docs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn’t the experience a user wants.

As a user, I expect that when I install a tool (direnv) and a repo supports it (nixfmt), then the tool should be used. Don't want direnv? Don't install it, then.

I need different environment variables & scripts specific to me

Great, direnv has support for that: $XDG_CONFIG_HOME/direnv/direnvrc and $XDG_CONFIG_HOME/direnv/lib/*.sh. We could also do this but that has security implications.

To be honest, I have never heard from any developer who has direnv installed that they don't want to use it (why did they install it then?). To me it's as simple as:

  • Only editing the readme once? Ignore the warning
  • Doing something in the repo often? direnv deny it once. It's quick and easy
  • Doing something in the repo often but you are like me? Ignore the warning every time

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please don't block this PR on that, I think everyone can handle whatever outcome we end up with 🙈 Thank you for your work!! :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I have never heard from any developer who has direnv installed that they don't want to use it (why did they install it then?). To me it's as simple as:

  • Only editing the readme once? Ignore the warning
  • Doing something in the repo often? direnv deny it once. It's quick and easy
  • Doing something in the repo often but you are like me? Ignore the warning every time

@dasJ Late to the party, but I just wanted to say I strongly discourage adding .envrc to a git repo (or any other VCS), and it's not about having direnv installed and not wanting to use it btw :) Here are some counter arguments:

  • I want to have my own nix shell and env vars when working on this repo (or any other), for which I would like to be in control of my own .envrc file.
    • Specifically on this project, what if I want to use a different Haskell LSP server version? or a different Cabal version? Or if I need some other package in this environment that the project assumes it's globally available?
  • I want to be able to source other local files relevant to my environment on that .envrc, e.g. I use fish shell and it requires some tweaks on certain repos, other folks use zsh or bash, etc.

Having this file committed to the repo removes this flexibility, but it's also easier to argue in its favor for an open source public repo. For private repos there's even more counter arguments, for instance, we have this at work:

  • Need to set AWS secrets on a daily basis (lasts 24 hours).
  • Need to set specific secrets that last a year (can be sourced from a local file).
  • Still need specific Nix shells at times (every project is different).
  • Need project-specific env vars that need to change depending on what we are testing and on which environment.

What I suggest instead

Let the users decide what they want available in their environments while still leveraging direnv, be a good citizen and add .envrc to your global .gitignore or add it to the project's .gitignore to avoid anyone adding it back.

Copy link

@adrian-gierakowski adrian-gierakowski Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or if I need some other package in this environment that the project assumes it's globally available?

a good project shell would assume no such things

Regarding credentials: these should not be set in .envrc, but project shell should provide tools (scripts, commands) to manage these

Still need specific Nix shells at times (every project is different).

These can be entered ad-hoc with nix shell\develop

Need project-specific env vars that need to change depending on what we are testing and on which environment.

A command (provided by default dev shell) should be provided to switch between envs

.envrc IMHO should never be edited by devs, it should simply setup all tools needed to work on given project. If customisation of env vars is needed this could be loaded from optional, gitignored file(s)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a good project shell would assume no such things

This project assumes I need a specific HLS version

Regarding credentials: these should not be set in .envrc, but project shell should provide tools (scripts, commands) to manage these

Agree to disagree. The methods vary across different projects when working on a big organization. A lot of flows are very complex and unfortunately can't be automated, e.g. sign in via SSO behind a VPN, approving the sign in attempt via Okta / Duo, etc. It barely works on Linux, let alone trying to automate these things.

It's much easier on open source public projects, I'll give you that, but private organizations are a completely different story.

These can be entered ad-hoc with nix shell\develop

It doesn't make it easier to pin specific pkgs, add overlays, etc.

A command (provided by default dev shell) should be provided to switch between envs

This isn't bad actually, might work well in some cases, will give it a try :) Certainly not in cases where switching envs requires going through the complex authorization flows, though.

If customisation of env vars is needed this could be loaded from optional, gitignored file(s)

How would you do this if you don't control the content of .envrc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've had this topic at least thrice already. Not sure having this discussion yet another time is a productive use of anybody's time

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
/dist-newstyle
/.ghc.environment.*
/result
/.direnv
44 changes: 31 additions & 13 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-22.05";
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-23.05";

flake-utils.url = "github:numtide/flake-utils";

Expand Down Expand Up @@ -84,6 +84,7 @@
# nixfmt: expand
cabal-install
stylish-haskell
shellcheck
]);
});

Expand Down
6 changes: 3 additions & 3 deletions main/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ data Nixfmt = Nixfmt

options :: Nixfmt
options =
let defaultWidth = 80
let defaultWidth = 100
addDefaultHint value message =
message ++ "\n[default: " ++ show value ++ "]"
in Nixfmt
{ files = [] &= args &= typ "FILES"
, width =
defaultWidth &=
help (addDefaultHint defaultWidth "Maximum width in characters")
, check = False &= help "Check whether files are formatted"
, check = False &= help "Check whether files are formatted without modifying them"
, quiet = False &= help "Do not report errors"
, verify =
False &=
help
"Check that the output parses and formats the same as the input"
"Apply sanity checks on the output after formatting"
} &=
summary ("nixfmt v" ++ showVersion version) &=
help "Format Nix source code"
Expand Down
16 changes: 9 additions & 7 deletions nixfmt.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ executable nixfmt
else
buildable: True
build-depends:
base >= 4.12.0 && < 4.17
base >= 4.12.0 && < 4.19
, cmdargs >= 0.10.20 && < 0.11
, nixfmt
, unix >= 2.7.2 && < 2.8
, text >= 1.2.3 && < 1.3
, unix >= 2.7.2 && < 2.9
, text >= 1.2.3 && < 2.2

-- for System.IO.Atomic
, directory >= 1.3.3 && < 1.4
Expand Down Expand Up @@ -86,11 +86,13 @@ library

hs-source-dirs: src
build-depends:
base >= 4.12.0 && < 4.17
, megaparsec >= 9.0.1 && < 9.3
base >= 4.12.0 && < 4.19
, megaparsec >= 9.0.1 && < 9.6
, mtl
, parser-combinators >= 1.0.3 && < 1.4
, scientific >= 0.3.0 && < 0.4.0
, text >= 1.2.3 && < 1.3
, text >= 1.2.3 && < 2.2
, transformers
default-language: Haskell2010
ghc-options:
-Wall
Expand All @@ -113,7 +115,7 @@ executable js-interface
-Wredundant-constraints
-Wno-orphans
build-depends:
base >= 4.12.0 && < 4.17
base >= 4.12.0 && < 4.19
, ghcjs-base >= 0.2.0 && < 0.3
, nixfmt
hs-source-dirs: js/
Expand Down
44 changes: 37 additions & 7 deletions src/Nixfmt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ module Nixfmt
, formatVerify
) where

import Data.Either (fromRight)
import Data.Bifunctor (bimap, first)
import Data.Text (Text)
import Data.Text (Text, unpack)
import qualified Text.Megaparsec as Megaparsec (parse)
import Text.Megaparsec.Error (errorBundlePretty)

import Nixfmt.Parser (file)
import Nixfmt.Predoc (layout)
import Nixfmt.Pretty ()
import Nixfmt.Types (ParseErrorBundle)
import Nixfmt.Types (ParseErrorBundle, Whole(..), Expression, walkSubprograms)

-- import Debug.Trace (traceShow, traceShowId)

type Width = Int

Expand All @@ -31,17 +34,44 @@ format width filename
= bimap errorBundlePretty (layout width)
. Megaparsec.parse file filename

-- Same functionality as `format`, but add sanity checks to guarantee the following properties of the formatter:
-- - Correctness: The formatted output parses, and the parse tree is identical to the input's
-- - Idempotency: Formatting the output again will not modify it
--
-- If any issues are found, the operation will fail and print an error message. It will contain a diff showcasing
-- the issue on an automatically minimized example based on the input.
formatVerify :: Width -> FilePath -> Text -> Either String Text
formatVerify width path unformatted = do
unformattedParsed <- parse unformatted
unformattedParsed@(Whole unformattedParsed' _) <- parse unformatted
let formattedOnce = layout width unformattedParsed
formattedOnceParsed <- parse formattedOnce
formattedOnceParsed <- flip first (parse formattedOnce) $
(\x -> pleaseReport "Fails to parse after formatting.\n" <> x <> "\n\nAfter Formatting:\n" <> unpack formattedOnce)
let formattedTwice = layout width formattedOnceParsed
if formattedOnceParsed /= unformattedParsed
then pleaseReport "Parses differently after formatting."
then Left $
let
minimized = minimize unformattedParsed' (\e -> parse (layout width e) == Right (Whole e []))
in
pleaseReport "Parses differently after formatting."
<> "\n\nBefore formatting:\n" <> (show minimized)
<> "\n\nAfter formatting:\n" <> (show $ fromRight (error "TODO") $ parse (layout width minimized))
else if formattedOnce /= formattedTwice
then pleaseReport "Nixfmt is not idempotent."
then Left $
let
minimized = minimize unformattedParsed'
(\e -> layout width e == layout width (fromRight (error "TODO") $ parse $ layout width e))
in
pleaseReport "Nixfmt is not idempotent."
<> "\n\nAfter one formatting:\n" <> unpack (layout width minimized)
<> "\n\nAfter two:\n" <> unpack (layout width (fromRight (error "TODO") $ parse $ layout width minimized))
else Right formattedOnce
where
parse = first errorBundlePretty . Megaparsec.parse file path
pleaseReport x = Left $ path <> ": " <> x <> " This is a bug in nixfmt. Please report it at https://github.com/serokell/nixfmt"
pleaseReport x = path <> ": " <> x <> " This is a bug in nixfmt. Please report it at https://github.com/serokell/nixfmt"


minimize :: Expression -> (Expression -> Bool) -> Expression
minimize expr test =
case concatMap (\e -> case test e of { False -> [minimize e test]; True -> [] }) $ walkSubprograms expr of
result:_ -> result
[] -> expr
Loading