diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -5,6 +5,16 @@
 ## Unreleased changes
 
 
+## Version 0.6.0.0
+
+-   **elynx-tree:** remove parallel folds with layers (`parBranchFoldMapWithLayer`
+    too special and slow).
+-   **elynx-tree:** fix various tree instances; add zip trees with appropriate
+    instances.
+-   Remove `monad-logger` dependency and implement lighter alternative.
+-   Significant changes to the tool chain.
+
+
 ## Version 0.5.1.0
 
 -   **elynx-tree:** new functions `isValidPath`, `isLeaf`, `depth`; add conversion
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 # The ELynx Suite
 
-Version: 0.5.1.0.
+Version: 0.6.0.0.
 Reproducible evolution made easy.
 
 <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>
@@ -39,7 +39,7 @@
 
 # Installation
 
-ELynx is written in [Haskell](https://www.haskell.org/) and can be installed with [Stack](https://docs.haskellstack.org/en/stable/README/).
+ELynx is written in [Haskell](https://www.haskell.org/) and can be installed with [cabal-install](https://cabal.readthedocs.io/en/3.4/cabal-commands.html) or [Stack](https://docs.haskellstack.org/en/stable/README/).
 
 1.  Install Stack with your package manager, or directly from the web
     page.
@@ -67,15 +67,15 @@
     [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable. Then, they can be used directly.
 
 
-# SLynx
+# Get help
 
-Handle evolutionary sequences.
+For example:
 
-    stack exec slynx -- --help | head -n -16
+    slynx --help
 
-    ELynx Suite version 0.5.1.0.
+    ELynx Suite version 0.6.0.0.
     Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
+    Compiled on September 3, 2021, at 20:56 pm, UTC.
     
     Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] 
                  [-f|--force] [--no-elynx-file] COMMAND
@@ -84,7 +84,7 @@
     Available options:
       -h,--help                Show this help text
       -V,--version             Show version
-      -v,--verbosity VALUE     Be verbose; one of: Quiet Warning Info Debug
+      -v,--verbosity VALUE     Be verbose; one of: Quiet Warn Info Debug
                                (default: Info)
       -o,--output-file-basename NAME
                                Specify base name of output file
@@ -112,107 +112,33 @@
       - Protein (amino acids)
       - ProteinX (amino acids; including gaps)
       - ProteinS (amino acids; including gaps, and translation stops)
-
-
-## Concatenate
-
-Concatenate multi sequence alignments.
-
-    stack exec slynx -- concatenate --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE
-      Concatenate sequences found in input files.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      -h,--help                Show this help text
-
-
-## Examine
-
-Examine sequence with `slynx examine`.
-
-    stack exec slynx -- examine --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site]
-      Examine sequences. If data is a multi sequence alignment, additionally analyze columns.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      --per-site               Report per site summary statistics
-      -h,--help                Show this help text
-
-
-## Filter
-
-Filter sequences with `filer-rows`.
-
-    stack exec slynx -- filter-rows --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH] 
-                             [--shorter-than LENGTH] [--standard-characters]
-      Filter rows (or sequences) found in input files.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      --longer-than LENGTH     Only keep sequences longer than LENGTH
-      --shorter-than LENGTH    Only keep sequences shorter than LENGTH
-      --standard-characters    Only keep sequences containing at least one standard
-                               (i.e., non-IUPAC) character
-      -h,--help                Show this help text
-
-Filter columns of multi sequence alignments with `filter-columns`.
-
-    stack exec slynx -- filter-columns --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
+      - ProteinI (amino acids; including gaps, translation stops, and IUPAC codes)
+    ELynx
+    -----
+    A Haskell library and tool set for computational biology. The goal of ELynx is
+    reproducible research. Evolutionary sequences and phylogenetic trees can be
+    read, viewed, modified and simulated. The command line with all arguments is
+    logged consistently, and automatically. Data integrity is verified using SHA256
+    sums so that validation of past analyses is possible without the need to
+    recompute the result.
     
-    Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE 
-                                [--standard-chars DOUBLE]
-      Filter columns of multi sequence alignments.
+    slynx     Analyze, modify, and simulate evolutionary sequences.
+    tlynx     Analyze, modify, and simulate phylogenetic trees.
+    elynx     Validate and redo past analyses.
     
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      --standard-chars DOUBLE  Keep columns with a proportion standard (non-IUPAC)
-                               characters larger than DOUBLE in [0,1]
-      -h,--help                Show this help text
+    Get help for sub commands:
+      slynx examine --help
 
 
-## Simulate
+## Sub command
 
-Simulate sequences with `slynx simulate`.
+The documentation of sub commands can be accessed separately:
 
-    stack exec slynx -- simulate --help
+    slynx simulate --help
 
-    ELynx Suite version 0.5.1.0.
+    ELynx Suite version 0.6.0.0.
     Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
+    Compiled on September 3, 2021, at 20:56 pm, UTC.
     
     Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL] 
                           [-m|--mixture-model MODEL] [-e|--edm-file NAME] 
@@ -281,315 +207,4 @@
          LG exchangeabilities with site profiles (Phylobayes) given in FILES.
          -m "EDM(LG-Custom)" -p FILES
     For special mixture models, mixture weights are optional.
-
-
-## Sub-sample
-
-Sub-sample columns from multi sequence alignments.
-
-    stack exec slynx -- sub-sample --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE
-                            (-n|--number-of-sites INT)
-                            (-m|--number-of-alignments INT) [-S|--seed [INT]]
-      Sub-sample columns from multi sequence alignments.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      -n,--number-of-sites INT Number of sites randomly drawn with replacement
-      -m,--number-of-alignments INT
-                               Number of multi sequence alignments to be created
-      -S,--seed [INT]          Seed for random number generator; list of 32 bit
-                               integers with up to 256 elements (default: random)
-      -h,--help                Show this help text
-    
-    Create a given number of multi sequence alignments, each of which contains a given number of random sites drawn from the original multi sequence alignment.
-
-
-## Translate
-
-Translate sequences.
-
-    stack exec slynx -- translate --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT)
-                           (-u|--universal-code CODE)
-      Translate from DNA to Protein or DNAX to ProteinX.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -a,--alphabet NAME       Specify alphabet type NAME
-      INPUT-FILE               Read sequences from INPUT-FILE
-      -r,--reading-frame INT   Reading frame [0|1|2].
-      -u,--universal-code CODE universal code; one of: Standard,
-                               VertebrateMitochondrial.
-      -h,--help                Show this help text
-
-
-# TLynx
-
-Handle phylogenetic trees in Newick format.
-
-    stack exec tlynx -- --help | head -n -16
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] 
-                 [-f|--force] [--no-elynx-file] COMMAND
-      Compare, examine, and simulate phylogenetic trees.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -v,--verbosity VALUE     Be verbose; one of: Quiet Warning Info Debug
-                               (default: Info)
-      -o,--output-file-basename NAME
-                               Specify base name of output file
-      -f,--force               Ignore previous analysis and overwrite existing
-                               output files.
-      --no-elynx-file          Do not write data required to reproduce an analysis.
-    
-    Available commands:
-      compare                  Compare two phylogenetic trees (compute distances and branch-wise differences).
-      connect                  Connect two phylogenetic trees in all ways (possibly honoring constraints).
-      distance                 Compute distances between many phylogenetic trees.
-      examine                  Compute summary statistics of phylogenetic trees.
-      shuffle                  Shuffle a phylogenetic tree (keep coalescent times, but shuffle topology and leaves).
-      simulate                 Simulate phylogenetic trees using a birth and death or coalescent process.
-    
-    
-    Available tree file formats:
-      - Newick Standard: Branch support values are stored in square brackets after branch lengths.
-      - Newick IqTree:   Branch support values are stored as node names after the closing bracket of forests.
-      - Newick RevBayes: Key-value pairs is provided in square brackets after node names as well as branch lengths. XXX: Key value pairs are ignored at the moment.
-
-
-## Compare
-
-Compute distances between phylogenetic trees.
-
-    stack exec tlynx -- compare --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect] 
-                         [-f|--newick-format FORMAT] NAMES
-      Compare two phylogenetic trees (compute distances and branch-wise differences).
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -n,--normalize           Normalize trees before comparison
-      -b,--bipartitions        Print and plot common and missing bipartitions
-      -t,--intersect           Compare intersections; i.e., before comparison, drop
-                               leaves that are not present in the other tree
-      -f,--newick-format FORMAT
-                               Newick tree format: Standard, IqTree, or RevBayes;
-                               default: Standard; for detailed help, see 'tlynx
-                               --help'
-      NAMES                    Tree files
-      -h,--help                Show this help text
-
-
-## Connect
-
-Connect two phylogenetic tree in all ways (possibly honoring constraints).
-
-    stack exec tlynx -- connect --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx connect [-f|--newick-format FORMAT] [-c|--contraints CONSTRAINTS]
-                         TREE-FILE-A TREE-FILE-B
-      Connect two phylogenetic trees in all ways (possibly honoring constraints).
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -f,--newick-format FORMAT
-                               Newick tree format: Standard, IqTree, or RevBayes;
-                               default: Standard; for detailed help, see 'tlynx
-                               --help'
-      -c,--contraints CONSTRAINTS
-                               File containing one or more Newick trees to be used
-                               as constraints
-      TREE-FILE-A              File containing the first Newick tree
-      TREE-FILE-B              File containing the second Newick tree
-      -h,--help                Show this help text
-
-
-## Distancce
-
-Compute distances between many phylogenetic trees.
-
-    stack exec tlynx -- distance --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx distance (-d|--distance MEASURE) [-n|--normalize] [-t|--intersect] 
-                          [-s|--summary-statistics] 
-                          [-m|--master-tree-file MASTER-TREE-File] 
-                          [-f|--newick-format FORMAT] [INPUT-FILES]
-      Compute distances between many phylogenetic trees.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -d,--distance MEASURE    Type of distance to calculate (available distance
-                               measures are listed below)
-      -n,--normalize           Normalize trees before distance calculation; only
-                               affect distances depending on branch lengths
-      -t,--intersect           Compare intersections; i.e., before comparison, drop
-                               leaves that are not present in the other tree
-      -s,--summary-statistics  Report summary statistics only
-      -m,--master-tree-file MASTER-TREE-File
-                               Compare all trees to the tree in the master tree
-                               file.
-      -f,--newick-format FORMAT
-                               Newick tree format: Standard, IqTree, or RevBayes;
-                               default: Standard; for detailed help, see 'tlynx
-                               --help'
-      INPUT-FILES              Read tree(s) from INPUT-FILES; if more files are
-                               given, one tree is expected per file
-      -h,--help                Show this help text
-    
-    Distance measures:
-      symmetric                Symmetric distance (Robinson-Foulds distance).
-      incompatible-split[VAL]  Incompatible split distance. Collapse branches with (normalized)
-                               support less than 0.0<=VAL<=1.0 before distance calculation;
-                               if, let's say, VAL>0.7, only well supported differences contribute
-                               to the total distance.
-      branch-score             Branch score distance.
-
-
-## Examine
-
-Compute summary statistics of phylogenetic trees.
-
-    stack exec tlynx -- examine --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT]
-      Compute summary statistics of phylogenetic trees.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      INPUT-FILE               Read trees from INPUT-FILE
-      -f,--newick-format FORMAT
-                               Newick tree format: Standard, IqTree, or RevBayes;
-                               default: Standard; for detailed help, see 'tlynx
-                               --help'
-      -h,--help                Show this help text
-
-
-## Shuffle
-
-Shuffle a phylogenetic tree (keep coalescent times, but shuffle topology and
-leaves).
-
-    stack exec tlynx -- shuffle --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx shuffle [-f|--newick-format FORMAT] [-n|--replicates N] TREE-FILE 
-                         [-S|--seed [INT]]
-      Shuffle a phylogenetic tree (keep coalescent times, but shuffle topology and leaves).
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -f,--newick-format FORMAT
-                               Newick tree format: Standard, IqTree, or RevBayes;
-                               default: Standard; for detailed help, see 'tlynx
-                               --help'
-      -n,--replicates N        Number of trees to generate
-      TREE-FILE                File containing a Newick tree
-      -S,--seed [INT]          Seed for random number generator; list of 32 bit
-                               integers with up to 256 elements (default: random)
-      -h,--help                Show this help text
-
-
-## Simulate
-
-Simulate phylogenetic trees using birth and death processes.
-
-    stack exec tlynx -- simulate --help
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: tlynx simulate (-t|--nTrees INT) (-n|--nLeaves INT) PROCESS 
-                          [-u|--sub-sample DOUBLE] [-s|--summary-statistics] 
-                          [-S|--seed [INT]]
-      Simulate phylogenetic trees using a birth and death or coalescent process.
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-      -t,--nTrees INT          Number of trees
-      -n,--nLeaves INT         Number of leaves per tree
-      -u,--sub-sample DOUBLE   Perform sub-sampling; see below.
-      -s,--summary-statistics  For each branch, print length and number of children
-      -S,--seed [INT]          Seed for random number generator; list of 32 bit
-                               integers with up to 256 elements (default: random)
-      -h,--help                Show this help text
-    
-    Available processes:
-      birthdeath               Birth and death process
-      coalescent               Coalescent process
-    
-    See, for example, 'tlynx simulate birthdeath --help'.
-    Sub-sample with probability p:
-      1. Simulate one big tree with n'=round(n/p), n'>=n, leaves;
-      2. Randomly sample sub-trees with n leaves.
-      (With p=1.0, the same tree is reported over and over again.)
-
-
-# ELynx
-
-Validate and (optionally) redo past ELynx analyses.
-
-    stack exec elynx -- --help | head -n -16
-
-    ELynx Suite version 0.5.1.0.
-    Developed by Dominik Schrempf.
-    Compiled on June 12, 2021, at 14:54 pm, UTC.
-    
-    Usage: elynx COMMAND
-      Validate and redo past ELynx analyses
-    
-    Available options:
-      -h,--help                Show this help text
-      -V,--version             Show version
-    
-    Available commands:
-      validate                 Validate an ELynx analysis
-      redo                     Redo an ELynx analysis
 
diff --git a/slynx.cabal b/slynx.cabal
--- a/slynx.cabal
+++ b/slynx.cabal
@@ -1,77 +1,77 @@
-cabal-version:      2.2
-name:               slynx
-version:            0.5.1.1
-license:            GPL-3.0-or-later
-license-file:       LICENSE
-copyright:          Dominik Schrempf (2021)
-maintainer:         dominik.schrempf@gmail.com
-author:             Dominik Schrempf
-homepage:           https://github.com/dschrempf/elynx#readme
-bug-reports:        https://github.com/dschrempf/elynx/issues
-synopsis:           Handle molecular sequences
-description:
-    Examine, modify, and simulate molecular sequences in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>.
+cabal-version:  2.2
+name:           slynx
+version:        0.6.0.0
+synopsis:       Handle molecular sequences
+description:    Examine, modify, and simulate molecular sequences in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>.
+category:       Bioinformatics
+homepage:       https://github.com/dschrempf/elynx#readme
+bug-reports:    https://github.com/dschrempf/elynx/issues
+author:         Dominik Schrempf
+maintainer:     dominik.schrempf@gmail.com
+copyright:      Dominik Schrempf (2021)
+license:        GPL-3.0-or-later
+license-file:   LICENSE
+build-type:     Simple
 
-category:           Bioinformatics
-build-type:         Simple
 extra-source-files:
     README.md
     ChangeLog.md
 
 source-repository head
-    type:     git
-    location: https://github.com/dschrempf/elynx
+  type: git
+  location: https://github.com/dschrempf/elynx
 
 library
-    exposed-modules:
-        SLynx.Concatenate.Concatenate
-        SLynx.Concatenate.Options
-        SLynx.Examine.Examine
-        SLynx.Examine.Options
-        SLynx.Filter.Filter
-        SLynx.Filter.Options
-        SLynx.Options
-        SLynx.Simulate.Options
-        SLynx.Simulate.PhyloModel
-        SLynx.Simulate.Simulate
-        SLynx.SLynx
-        SLynx.SubSample.Options
-        SLynx.SubSample.SubSample
-        SLynx.Tools
-        SLynx.Translate.Options
-        SLynx.Translate.Translate
-
-    hs-source-dirs:   src
-    other-modules:    Paths_slynx
-    autogen-modules:  Paths_slynx
-    default-language: Haskell2010
-    ghc-options:      -Wall -Wunused-packages
-    build-depends:
-        attoparsec >=0.13.2.5,
-        base >=4.7 && <5,
-        bytestring >=0.10.12.0,
-        containers >=0.6.2.1,
-        elynx-markov >=0.5.1.1,
-        elynx-seq >=0.5.1.1,
-        elynx-tools >=0.5.1.1,
-        elynx-tree >=0.5.1.1,
-        hmatrix >=0.20.2,
-        monad-logger >=0.3.36,
-        mwc-random >=0.15.0.1,
-        optparse-applicative >=0.16.1.0,
-        statistics >=0.15.2.0,
-        text >=1.2.4.1,
-        transformers >=0.5.6.2,
-        vector >=0.12.3.0
+  exposed-modules:
+      SLynx.Concatenate.Concatenate
+      SLynx.Concatenate.Options
+      SLynx.Examine.Examine
+      SLynx.Examine.Options
+      SLynx.Filter.Filter
+      SLynx.Filter.Options
+      SLynx.Options
+      SLynx.Simulate.Options
+      SLynx.Simulate.PhyloModel
+      SLynx.Simulate.Simulate
+      SLynx.SLynx
+      SLynx.SubSample.Options
+      SLynx.SubSample.SubSample
+      SLynx.Tools
+      SLynx.Translate.Options
+      SLynx.Translate.Translate
+  other-modules:
+      Paths_slynx
+  autogen-modules:
+      Paths_slynx
+  hs-source-dirs: src
+  ghc-options: -Wall -Wunused-packages
+  build-depends:
+      attoparsec
+    , base >=4.7 && <5
+    , bytestring
+    , containers
+    , elynx-markov
+    , elynx-seq
+    , elynx-tools
+    , elynx-tree
+    , hmatrix
+    , mwc-random
+    , optparse-applicative
+    , statistics
+    , text
+    , transformers
+    , vector
+  default-language: Haskell2010
 
 executable slynx
-    main-is:          Main.hs
-    hs-source-dirs:   app
-    other-modules:    Paths_slynx
-    default-language: Haskell2010
-    ghc-options:
-        -Wall -Wunused-packages -threaded -rtsopts -with-rtsopts=-N
-
-    build-depends:
-        base >=4.7 && <5,
-        slynx -any
+  main-is: Main.hs
+  other-modules:
+      Paths_slynx
+  autogen-modules:
+      Paths_slynx
+  hs-source-dirs: app
+  ghc-options: -Wall -Wunused-packages -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , slynx
+  default-language: Haskell2010
diff --git a/src/SLynx/Concatenate/Concatenate.hs b/src/SLynx/Concatenate/Concatenate.hs
--- a/src/SLynx/Concatenate/Concatenate.hs
+++ b/src/SLynx/Concatenate/Concatenate.hs
@@ -17,8 +17,7 @@
   )
 where
 
-import Control.Monad.Logger
-import Control.Monad.Trans.Reader (ask)
+import Control.Monad.Trans.Reader
 import qualified ELynx.Data.Sequence.Sequence as S
 import ELynx.Export.Sequence.Fasta
 import ELynx.Tools
@@ -28,8 +27,7 @@
 -- | Concatenate sequences.
 concatenateCmd :: ELynx ConcatenateArguments ()
 concatenateCmd = do
-  (ConcatenateArguments al fps) <- local <$> ask
-  $(logInfo) "Command: Concatenate sequences."
+  (ConcatenateArguments al fps) <- reader localArguments
   sss <- mapM (readSeqs al) fps
   let result = sequencesToFasta $ S.concatSequences sss
   out "concatenated multi sequence alignment " result ".fasta"
diff --git a/src/SLynx/Examine/Examine.hs b/src/SLynx/Examine/Examine.hs
--- a/src/SLynx/Examine/Examine.hs
+++ b/src/SLynx/Examine/Examine.hs
@@ -18,8 +18,7 @@
   )
 where
 
-import Control.Monad.Logger
-import Control.Monad.Trans.Reader (ask)
+import Control.Monad.Trans.Reader
 import qualified Data.ByteString.Lazy.Char8 as BL
 import qualified Data.Set as S
 import qualified Data.Vector.Unboxed as V
@@ -157,8 +156,7 @@
 -- | Examine sequences.
 examineCmd :: ELynx ExamineArguments ()
 examineCmd = do
-  (ExamineArguments al inFile perSiteFlag) <- local <$> ask
-  $(logInfo) "Command: Examine sequences."
+  (ExamineArguments al inFile perSiteFlag) <- localArguments <$> ask
   ss <- readSeqs al inFile
   let result = examine perSiteFlag ss
   out "result of examination" result ".out"
diff --git a/src/SLynx/Filter/Filter.hs b/src/SLynx/Filter/Filter.hs
--- a/src/SLynx/Filter/Filter.hs
+++ b/src/SLynx/Filter/Filter.hs
@@ -19,11 +19,9 @@
 where
 
 import Control.Monad (when)
-import Control.Monad.Logger
-import Control.Monad.Trans.Reader (ask)
+import Control.Monad.Trans.Reader
 import qualified Data.ByteString.Lazy.Char8 as BL
 import Data.Maybe (fromMaybe)
-import qualified Data.Text as T
 import qualified ELynx.Data.Sequence.Alignment as M
 import qualified ELynx.Data.Sequence.Sequence as S
 import ELynx.Export.Sequence.Fasta
@@ -41,22 +39,21 @@
 -- | Filter sequences.
 filterRowsCmd :: ELynx FilterRowsArguments ()
 filterRowsCmd = do
-  (FilterRowsArguments al inFile long short std) <- local <$> ask
-  $(logInfo) "Command: Filter sequences of a list of sequences."
+  (FilterRowsArguments al inFile long short std) <- localArguments <$> ask
   maybe
     (return ())
     ( \val ->
-        $(logInfo) $ T.pack $ "  Keep sequences longer than " <> show val <> "."
+        logInfoS $ "  Keep sequences longer than " <> show val <> "."
     )
     long
   maybe
     (return ())
     ( \val ->
-        $(logInfo) $ T.pack $ "  Keep sequences shorter than " <> show val <> "."
+        logInfoS $ "  Keep sequences shorter than " <> show val <> "."
     )
     short
   when std $
-    $(logInfo)
+    logInfoS
       "  Keep sequences containing at least one standard (i.e., non-IUPAC) character."
   ss <- readSeqs al inFile
   let result = filterRows long short std ss
@@ -71,16 +68,14 @@
 -- | Filter columns.
 filterColsCmd :: ELynx FilterColsArguments ()
 filterColsCmd = do
-  (FilterColsArguments al inFile standard) <- local <$> ask
-  $(logInfo) "Command: Filter columns of a multi sequence alignment."
+  (FilterColsArguments al inFile standard) <- localArguments <$> ask
   case standard of
     Nothing -> return ()
     Just p ->
-      $(logInfo) $
-        T.pack $
-          "  Keep columns with a proportion of standard (non-IUPAC) characters larger than "
-            ++ show p
-            ++ "."
+      logInfoS $
+        "  Keep columns with a proportion of standard (non-IUPAC) characters larger than "
+          ++ show p
+          ++ "."
   ss <- readSeqs al inFile
   let result = filterCols standard ss
   out "filtered sequences" result ".fasta"
diff --git a/src/SLynx/SLynx.hs b/src/SLynx/SLynx.hs
--- a/src/SLynx/SLynx.hs
+++ b/src/SLynx/SLynx.hs
@@ -27,35 +27,15 @@
 -- | Run SLynx with given arguments.
 slynx :: Arguments CommandArguments -> IO ()
 slynx c = case local c of
-  Concatenate _ ->
-    eLynxWrapper
-      c
-      (\(Arguments g (Concatenate l)) -> Arguments g l)
-      concatenateCmd
-  Examine _ ->
-    eLynxWrapper c (\(Arguments g (Examine l)) -> Arguments g l) examineCmd
-  FilterCols _ ->
-    eLynxWrapper
-      c
-      (\(Arguments g (FilterCols l)) -> Arguments g l)
-      filterColsCmd
-  FilterRows _ ->
-    eLynxWrapper
-      c
-      (\(Arguments g (FilterRows l)) -> Arguments g l)
-      filterRowsCmd
-  Simulate _ ->
-    eLynxWrapper c (\(Arguments g (Simulate l)) -> Arguments g l) simulateCmd
-  SubSample _ ->
-    eLynxWrapper
-      c
-      (\(Arguments g (SubSample l)) -> Arguments g l)
-      subSampleCmd
-  Translate _ ->
-    eLynxWrapper
-      c
-      (\(Arguments g (Translate l)) -> Arguments g l)
-      translateCmd
+  Concatenate l -> eLynxWrapper g l Concatenate concatenateCmd
+  Examine l -> eLynxWrapper g l Examine examineCmd
+  FilterCols l -> eLynxWrapper g l FilterCols filterColsCmd
+  FilterRows l -> eLynxWrapper g l FilterRows filterRowsCmd
+  Simulate l -> eLynxWrapper g l Simulate simulateCmd
+  SubSample l -> eLynxWrapper g l SubSample subSampleCmd
+  Translate l -> eLynxWrapper g l Translate translateCmd
+  where
+    g = global c
 
 -- | Run SLynx, parse arguments from command line.
 rSLynx :: IO ()
diff --git a/src/SLynx/Simulate/Options.hs b/src/SLynx/Simulate/Options.hs
--- a/src/SLynx/Simulate/Options.hs
+++ b/src/SLynx/Simulate/Options.hs
@@ -60,7 +60,7 @@
     argsMixtureWeights :: Maybe [Double],
     argsGammaParams :: Maybe GammaRateHeterogeneityParams,
     argsLength :: Int,
-    argsSeed :: Seed
+    argsSeed :: SeedOpt
   }
   deriving (Eq, Show, Generic)
 
@@ -70,7 +70,7 @@
     (maybeToList (argsEDMFile a) ++ fromMaybe [] (argsSiteprofilesFiles a))
   outSuffixes _ = [".model.gz", ".fasta"]
   getSeed = Just . argsSeed
-  setSeed a s = a {argsSeed = Fixed s}
+  setSeed a s = a {argsSeed = s}
   parser = simulateArguments
   cmdName = "simulate"
   cmdDsc = ["Simulate multi sequence alignments."]
diff --git a/src/SLynx/Simulate/Simulate.hs b/src/SLynx/Simulate/Simulate.hs
--- a/src/SLynx/Simulate/Simulate.hs
+++ b/src/SLynx/Simulate/Simulate.hs
@@ -20,7 +20,6 @@
 import Control.Applicative ((<|>))
 import Control.Monad
 import Control.Monad.IO.Class
-import Control.Monad.Logger
 import Control.Monad.Trans.Class
 import Control.Monad.Trans.Reader (ask)
 import qualified Data.ByteString.Builder as BB
@@ -28,9 +27,6 @@
 import Data.List
 import Data.Maybe
 import qualified Data.Set as Set
-import qualified Data.Text as T
-import qualified Data.Text.Lazy as LT
-import qualified Data.Text.Lazy.Encoding as LT
 import qualified Data.Vector as V
 import qualified Data.Vector.Storable as VS
 import qualified Data.Vector.Unboxed as U
@@ -66,7 +62,7 @@
 writeSiteDists :: [Int] -> V.Vector MR.StationaryDistribution -> ELynx SimulateArguments ()
 -- writeSiteDists is ds = out "site distributions of distribution mixture model" output ".sitedists"
 writeSiteDists componentIs ds = do
-  mbn <- outFileBaseName . global <$> ask
+  mbn <- outFileBaseName . globalArguments <$> ask
   case mbn of
     Nothing -> return ()
     Just bn -> liftIO $ BL.writeFile (bn <> ".sitedists") output
@@ -85,7 +81,7 @@
   GenIO ->
   ELynx SimulateArguments ()
 simulateAlignment pm t' n g = do
-  let t = fromLength . getLen <$> toTreeBranchLabels t'
+  let t = fromLength . getLength <$> toTreeBranchLabels t'
   leafStates <- case pm of
     MP.SubstitutionModel sm -> liftIO $ simulateAndFlattenPar n d e t g
       where
@@ -110,7 +106,7 @@
           | (sName, ss) <- zip leafNames leafStates
         ]
       output = sequencesToFasta sequences
-  $(logInfo) ""
+  logInfoS ""
   out "simulated multi sequence alignment" output ".fasta"
 
 -- Summarize EDM components; line to be printed to screen or log.
@@ -123,19 +119,19 @@
 
 reportModel :: MP.PhyloModel -> ELynx SimulateArguments ()
 reportModel m = do
-  as <- global <$> ask
+  as <- globalArguments <$> ask
   if writeElynxFile as
     then
       ( do
           let bn = outFileBaseName as
           case bn of
             Nothing ->
-              $(logInfo)
+              logInfoS
                 "No output file provided; omit writing machine-readable phylogenetic model."
             Just _ ->
               out "model definition (machine readable)" (BL.pack (show m) <> "\n") ".model.gz"
       )
-    else $(logInfo) "No elynx file required; omit writing machine-readable phylogenetic model."
+    else logInfoS "No elynx file required; omit writing machine-readable phylogenetic model."
 
 pretty :: Length -> String
 pretty = printf "%.5f" . fromLength
@@ -220,46 +216,45 @@
 -- | Simulate sequences.
 simulateCmd :: ELynx SimulateArguments ()
 simulateCmd = do
-  l <- local <$> ask
+  l <- localArguments <$> ask
   let treeFile = argsTreeFile l
-  $(logInfo) ""
-  $(logInfo) $ T.pack $ "Read tree from file '" ++ treeFile ++ "'."
+  logInfoS ""
+  logInfoS $ "Read tree from file '" ++ treeFile ++ "'."
   tree <- liftIO $ parseFileWith (newick Standard) treeFile
-  let t' = either error id $ phyloToLengthTree tree
-  $(logInfo) $ T.pack $ "Number of leaves: " ++ show (length $ leaves t')
-  $(logInfo) $ LT.toStrict $ LT.decodeUtf8 $ summarizeLengths t'
+  let t' = either error id $ toLengthTree tree
+  logInfoS $ "Number of leaves: " ++ show (length $ leaves t')
+  logInfoB $ summarizeLengths t'
   let edmFile = argsEDMFile l
   let sProfileFiles = argsSiteprofilesFiles l
-  $(logInfo) ""
-  $(logDebug) "Read EDM file or siteprofile files."
+  logInfoS ""
+  logDebugS "Read EDM file or siteprofile files."
   when (isJust edmFile && isJust sProfileFiles) $
     error "Got both: --edm-file and --siteprofile-files."
   edmCs <- case edmFile of
     Nothing -> return Nothing
     Just edmF -> do
-      $(logInfo) "Read EDM file."
+      logInfoS "Read EDM file."
       liftIO $ Just <$> parseFileWith phylobayes edmF
   maybe
     (return ())
-    ($(logInfo) . LT.toStrict . LT.decodeUtf8 . summarizeEDMComponents)
+    (logInfoB . summarizeEDMComponents)
     edmCs
   sProfiles <- case sProfileFiles of
     Nothing -> return Nothing
     Just fns -> do
-      $(logInfo) $
-        T.pack $
-          "Read siteprofiles from "
-            ++ show (length fns)
-            ++ " file(s)."
-      $(logDebug) $ T.pack $ "The file names are:" ++ show fns
+      logInfoS $
+        "Read siteprofiles from "
+          ++ show (length fns)
+          ++ " file(s)."
+      logDebugS $ "The file names are:" ++ show fns
       xs <- liftIO $ mapM (parseFileWith siteprofiles) fns
       return $ Just $ concat xs
   maybe
     (return ())
-    ($(logInfo) . LT.toStrict . LT.decodeUtf8 . summarizeEDMComponents)
+    (logInfoB . summarizeEDMComponents)
     sProfiles
   let edmCsOrSiteprofiles = edmCs <|> sProfiles
-  $(logInfo) "Read model string."
+  logInfoS "Read model string."
   let ms = argsSubstitutionModelString l
       mm = argsMixtureModelString l
       mws = argsMixtureWeights l
@@ -270,31 +265,19 @@
   let maybeGammaParams = argsGammaParams l
   phyloModel <- case maybeGammaParams of
     Nothing -> do
-      $(logInfo) $
-        LT.toStrict $
-          LT.decodeUtf8 $
-            BL.unlines $
-              summarizePM
-                phyloModel'
+      logInfoB $ BL.unlines $ summarizePM phyloModel'
       return phyloModel'
     Just (n, alpha) -> do
-      $(logInfo) $
-        LT.toStrict $
-          LT.decodeUtf8 $
-            BL.intercalate "\n" $
-              summarizePM phyloModel'
-      $(logInfo) ""
-      $(logInfo) $
-        LT.toStrict $
-          LT.decodeUtf8 $
-            BL.intercalate "\n" $
-              summarizeGammaRateHeterogeneity n alpha
+      logInfoB $ BL.intercalate "\n" $ summarizePM phyloModel'
+      logInfoS ""
+      logInfoB $ BL.intercalate "\n" $ summarizeGammaRateHeterogeneity n alpha
       return $ expand n alpha phyloModel'
   reportModel phyloModel
-  $(logInfo) "Simulate alignment."
+  logInfoS "Simulate alignment."
   let alignmentLength = argsLength l
-  $(logInfo) $ T.pack $ "Length: " <> show alignmentLength <> "."
-  gen <- case argsSeed l of
-    Random -> error "simulateCmd: seed not available; please contact maintainer."
-    Fixed s -> liftIO $ initialize s
+  logInfoS $ "Length: " <> show alignmentLength <> "."
+  gen <- liftIO <$> initialize $ case argsSeed l of
+    RandomUnset -> error "simulateCmd: seed not available; please contact maintainer."
+    RandomSet s -> s
+    Fixed s -> s
   simulateAlignment phyloModel t' alignmentLength gen
diff --git a/src/SLynx/SubSample/Options.hs b/src/SLynx/SubSample/Options.hs
--- a/src/SLynx/SubSample/Options.hs
+++ b/src/SLynx/SubSample/Options.hs
@@ -33,7 +33,7 @@
     ssInFile :: FilePath,
     ssNSites :: Int,
     ssNAlignments :: Int,
-    ssMbSeed :: Seed
+    ssMbSeed :: SeedOpt
   }
   deriving (Eq, Show, Generic)
 
@@ -55,7 +55,7 @@
   inFiles = pure . ssInFile
   outSuffixes a = getOutSuffixes (ssNAlignments a) "fasta"
   getSeed = Just . ssMbSeed
-  setSeed a s = a {ssMbSeed = Fixed s}
+  setSeed a s = a {ssMbSeed = s}
   parser = subSampleArguments
   cmdName = "sub-sample"
   cmdDsc = ["Sub-sample columns from multi sequence alignments."]
diff --git a/src/SLynx/SubSample/SubSample.hs b/src/SLynx/SubSample/SubSample.hs
--- a/src/SLynx/SubSample/SubSample.hs
+++ b/src/SLynx/SubSample/SubSample.hs
@@ -19,9 +19,7 @@
 
 import Control.Monad
 import Control.Monad.IO.Class
-import Control.Monad.Logger
 import Control.Monad.Trans.Reader (ask)
-import qualified Data.Text as T
 import qualified ELynx.Data.Sequence.Alignment as M
 import ELynx.Export.Sequence.Fasta
 import ELynx.Tools
@@ -32,14 +30,9 @@
 -- | Sub sample sequences.
 subSampleCmd :: ELynx SubSampleArguments ()
 subSampleCmd = do
-  (SubSampleArguments al inFile nSites nAlignments (Fixed s)) <- local <$> ask
-  $(logInfo) "Command: Sub sample from a multi sequence alignment."
-  $(logInfo) $ T.pack $ "  Sample " <> show nSites <> " sites."
-  $(logInfo) $
-    T.pack $
-      "  Sample "
-        <> show nAlignments
-        <> " multi sequence alignments."
+  (SubSampleArguments al inFile nSites nAlignments (Fixed s)) <- localArguments <$> ask
+  logInfoS $ "  Sample " <> show nSites <> " sites."
+  logInfoS $ "  Sample " <> show nAlignments <> " multi sequence alignments."
   ss <- readSeqs al inFile
   gen <- liftIO $ initialize s
   let a = either error id (M.fromSequences ss)
diff --git a/src/SLynx/Tools.hs b/src/SLynx/Tools.hs
--- a/src/SLynx/Tools.hs
+++ b/src/SLynx/Tools.hs
@@ -22,45 +22,25 @@
 where
 
 import Control.Monad.IO.Class
-import Control.Monad.Logger
-import qualified Data.Text as T
 import ELynx.Data.Alphabet.Alphabet
 import ELynx.Data.Sequence.Sequence
 import ELynx.Import.Sequence.Fasta
 import ELynx.Tools
 import Options.Applicative
 
--- -- | Read sequences of given alphabet from file or standard input.
--- readSeqs
---   :: (MonadIO m, MonadLogger m) => Alphabet -> Maybe FilePath -> m [Sequence]
--- readSeqs a mfp = do
---   case mfp of
---     Nothing ->
---       $(logInfo)
---         $  T.pack
---         $  "Read sequences from standard input; alphabet "
---         <> show a
---         <> "."
---     Just fp ->
---       $(logInfo)
---         $  T.pack
---         $  "Read sequences from file "
---         <> fp
---         <> "; alphabet "
---         <> show a
---         <> "."
---   liftIO $ parseFileOrIOWith (fasta a) mfp
-
 -- | Read sequences of given alphabet from file or standard input.
-readSeqs :: (MonadIO m, MonadLogger m) => Alphabet -> FilePath -> m [Sequence]
+readSeqs ::
+  (HasLock e, HasLogHandles e, HasVerbosity e) =>
+  Alphabet ->
+  FilePath ->
+  Logger e [Sequence]
 readSeqs a fp = do
-  $(logInfo) $
-    T.pack $
-      "Read sequences from file "
-        <> fp
-        <> "; alphabet "
-        <> show a
-        <> "."
+  logInfoS $
+    "Read sequences from file "
+      <> fp
+      <> "; alphabet "
+      <> show a
+      <> "."
   liftIO $ parseFileWith (fasta a) fp
 
 -- | Command line option to specify the alphabet. Used by various commands.
diff --git a/src/SLynx/Translate/Translate.hs b/src/SLynx/Translate/Translate.hs
--- a/src/SLynx/Translate/Translate.hs
+++ b/src/SLynx/Translate/Translate.hs
@@ -17,9 +17,7 @@
   )
 where
 
-import Control.Monad.Logger
-import Control.Monad.Trans.Reader (ask)
-import qualified Data.Text as T
+import Control.Monad.Trans.Reader
 import ELynx.Data.Character.Codon
 import ELynx.Data.Sequence.Sequence
 import ELynx.Data.Sequence.Translate
@@ -34,11 +32,10 @@
 -- | Translate sequences.
 translateCmd :: ELynx TranslateArguments ()
 translateCmd = do
-  (TranslateArguments al inFile rf uc) <- local <$> ask
-  $(logInfo) "Command: Translate sequences to amino acids."
-  $(logInfo) $ T.pack $ "  Universal code: " <> show uc <> "."
-  $(logInfo) $ T.pack $ "  Reading frame: " <> show rf <> "."
-  $(logInfo) ""
+  (TranslateArguments al inFile rf uc) <- localArguments <$> ask
+  logInfoS $ "  Universal code: " <> show uc <> "."
+  logInfoS $ "  Reading frame: " <> show rf <> "."
+  logInfoS ""
   ss <- readSeqs al inFile
   let result = sequencesToFasta $ translateSeqs rf uc ss
   out "translated sequences" result "fasta"
