diff --git a/src/main/java/de/prob2/jupyter/Parameter.java b/src/main/java/de/prob2/jupyter/Parameter.java
index ae7c214e30feb99d7b77d6af622de0b13e0ef02a..5368a3c229b9e950870de1ab3ff91283f1ec7a69 100644
--- a/src/main/java/de/prob2/jupyter/Parameter.java
+++ b/src/main/java/de/prob2/jupyter/Parameter.java
@@ -67,6 +67,24 @@ public abstract class Parameter<T> {
 		public abstract T validate(final @NotNull Parameter<T> param, final @NotNull List<@NotNull String> argValues);
 	}
 	
+	public static class RequiredSingle extends Parameter<@NotNull String> {
+		public RequiredSingle(final @NotNull String identifier, final boolean repeating, final @NotNull Parameter.Splitter splitter, final @NotNull Parameter.Validator<@NotNull String> validator) {
+			super(identifier, repeating, splitter, validator);
+		}
+	}
+	
+	public static class OptionalSingle extends Parameter<@NotNull Optional<String>> {
+		public OptionalSingle(final @NotNull String identifier, final boolean repeating, final @NotNull Parameter.Splitter splitter, final @NotNull Parameter.Validator<@NotNull Optional<String>> validator) {
+			super(identifier, repeating, splitter, validator);
+		}
+	}
+	
+	public static class Multiple extends Parameter<@NotNull List<@NotNull String>> {
+		public Multiple(final @NotNull String identifier, final boolean repeating, final @NotNull Parameter.Splitter splitter, final @NotNull Parameter.Validator<@NotNull List<@NotNull String>> validator) {
+			super(identifier, repeating, splitter, validator);
+		}
+	}
+	
 	private final @NotNull String identifier;
 	private final boolean repeating;
 	private final @NotNull Parameter.Splitter splitter;
@@ -106,4 +124,42 @@ public abstract class Parameter<T> {
 			.add("validator", this.getValidator())
 			.toString();
 	}
+	
+	public static Parameter.RequiredSingle required(final String identifier) {
+		return new Parameter.RequiredSingle(identifier, false, Parameter.Splitter.REGULAR, Parameter.Validator.EXACTLY_ONE);
+	}
+	
+	public static Parameter.OptionalSingle optional(final String identifier) {
+		return new Parameter.OptionalSingle(identifier, false, Parameter.Splitter.REGULAR, Parameter.Validator.ZERO_OR_ONE);
+	}
+	
+	public static Parameter.Multiple requiredMultiple(final String identifier) {
+		return new Parameter.Multiple(identifier, true, Parameter.Splitter.REGULAR, Parameter.Validator.ONE_OR_MORE);
+	}
+	
+	public static Parameter.Multiple optionalMultiple(final String identifier) {
+		return new Parameter.Multiple(identifier, true, Parameter.Splitter.REGULAR, Parameter.Validator.ZERO_OR_MORE);
+	}
+	
+	public static Parameter.RequiredSingle requiredRemainder(final String identifier) {
+		return new Parameter.RequiredSingle(identifier, false, Parameter.Splitter.REMAINDER, Parameter.Validator.EXACTLY_ONE);
+	}
+	
+	public static Parameter.OptionalSingle optionalRemainder(final String identifier) {
+		return new Parameter.OptionalSingle(identifier, false, Parameter.Splitter.REMAINDER, Parameter.Validator.ZERO_OR_ONE);
+	}
+	
+	public static Parameter.RequiredSingle body(final String identifier) {
+		return new Parameter.RequiredSingle(identifier, false, argString -> {
+			throw new AssertionError("Splitter of a body parameter should never be used");
+		}, (param, argValues) -> {
+			if (argValues.isEmpty()) {
+				throw new UserErrorException("Missing required body " + param.getIdentifier());
+			} else if (argValues.size() > 1) {
+				throw new AssertionError("Body " + param.getIdentifier() + " appeared more than once, this should never happen!");
+			}
+			
+			return argValues.get(0);
+		});
+	}
 }
diff --git a/src/main/java/de/prob2/jupyter/Parameters.java b/src/main/java/de/prob2/jupyter/Parameters.java
index e61339bd27859067beb91954ba3b5cc07f96f711..4d08aa6e7e2c94fc875a0c7f13f6773df504184e 100644
--- a/src/main/java/de/prob2/jupyter/Parameters.java
+++ b/src/main/java/de/prob2/jupyter/Parameters.java
@@ -11,9 +11,9 @@ public final class Parameters {
 	public static final @NotNull Parameters NONE = new Parameters(Collections.emptyList());
 	
 	private final @NotNull List<Parameter<?>> positionalParameters;
-	private final @Nullable PositionalParameter.RequiredRemainder bodyParam;
+	private final @Nullable Parameter.RequiredSingle bodyParam;
 	
-	public Parameters(final @NotNull List<Parameter<?>> positionalParameters, final @Nullable PositionalParameter.RequiredRemainder bodyParam) {
+	public Parameters(final @NotNull List<Parameter<?>> positionalParameters, final @Nullable Parameter.RequiredSingle bodyParam) {
 		super();
 		
 		this.positionalParameters = positionalParameters;
@@ -21,10 +21,10 @@ public final class Parameters {
 		boolean seenOptional = false;
 		boolean seenOnlyLast = false;
 		for (final Parameter<?> param : positionalParameters) {
-			final boolean isOptional = param instanceof PositionalParameter.OptionalSingle || param instanceof PositionalParameter.OptionalRemainder;
-			final boolean isOnlyLast = param instanceof PositionalParameter.RequiredRemainder || param instanceof PositionalParameter.OptionalRemainder;
+			final boolean isOptional = param.getValidator() == Parameter.Validator.ZERO_OR_ONE || param.getValidator() == Parameter.Validator.ZERO_OR_MORE;
+			final boolean isOnlyLast = param.isRepeating() || param.getSplitter() == Parameter.Splitter.REMAINDER;
 			if (seenOnlyLast) {
-				throw new IllegalArgumentException("A remainder positional parameter cannot be followed by any more positional parameters");
+				throw new IllegalArgumentException("A repeating or remainder positional parameter cannot be followed by any more positional parameters");
 			}
 			if (seenOptional && isOptional) {
 				throw new IllegalArgumentException("Required positional parameter " + param + " cannot follow an optional positional parameter");
@@ -44,7 +44,7 @@ public final class Parameters {
 		return this.positionalParameters;
 	}
 	
-	public @NotNull Optional<PositionalParameter.RequiredRemainder> getBodyParam() {
+	public @NotNull Optional<Parameter.RequiredSingle> getBodyParam() {
 		return Optional.ofNullable(this.bodyParam);
 	}
 }
diff --git a/src/main/java/de/prob2/jupyter/PositionalParameter.java b/src/main/java/de/prob2/jupyter/PositionalParameter.java
deleted file mode 100644
index 51fc846bd26a7cbb086d6106d103901b1dde4a66..0000000000000000000000000000000000000000
--- a/src/main/java/de/prob2/jupyter/PositionalParameter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package de.prob2.jupyter;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.jetbrains.annotations.NotNull;
-
-public final class PositionalParameter {
-	public static final class RequiredSingle extends Parameter<@NotNull String> {
-		public RequiredSingle(final @NotNull String identifier) {
-			super(identifier, false, Parameter.Splitter.REGULAR, Parameter.Validator.EXACTLY_ONE);
-		}
-	}
-	
-	public static final class OptionalSingle extends Parameter<@NotNull Optional<String>> {
-		public OptionalSingle(final @NotNull String identifier) {
-			super(identifier, false, Parameter.Splitter.REGULAR, Parameter.Validator.ZERO_OR_ONE);
-		}
-	}
-	
-	public static final class RequiredMultiple extends Parameter<@NotNull List<@NotNull String>> {
-		public RequiredMultiple(final @NotNull String identifier) {
-			super(identifier, true, Parameter.Splitter.REGULAR, Parameter.Validator.ONE_OR_MORE);
-		}
-	}
-	
-	public static final class OptionalMultiple extends Parameter<@NotNull List<@NotNull String>> {
-		public OptionalMultiple(final @NotNull String identifier) {
-			super(identifier, true, Parameter.Splitter.REGULAR, Parameter.Validator.ZERO_OR_MORE);
-		}
-	}
-	
-	public static final class RequiredRemainder extends Parameter<@NotNull String> {
-		public RequiredRemainder(final @NotNull String identifier) {
-			super(identifier, false, Parameter.Splitter.REMAINDER, Parameter.Validator.EXACTLY_ONE);
-		}
-	}
-	
-	public static final class OptionalRemainder extends Parameter<@NotNull Optional<String>> {
-		public OptionalRemainder(final @NotNull String identifier) {
-			super(identifier, false, Parameter.Splitter.REMAINDER, Parameter.Validator.ZERO_OR_ONE);
-		}
-	}
-	
-	private PositionalParameter() {
-		super();
-		
-		throw new AssertionError("Utility class");
-	}
-}
diff --git a/src/main/java/de/prob2/jupyter/commands/AssertCommand.java b/src/main/java/de/prob2/jupyter/commands/AssertCommand.java
index 55574b0cbf91a053b46f035f95e7ccc373aa7860..b886c6ff4af65425cb344c64bf8b62319ded8536 100644
--- a/src/main/java/de/prob2/jupyter/commands/AssertCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/AssertCommand.java
@@ -11,9 +11,9 @@ import de.prob.animator.domainobjects.FormulaExpand;
 import de.prob.statespace.AnimationSelector;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class AssertCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder FORMULA_PARAM = new PositionalParameter.RequiredRemainder("formula");
+	private static final @NotNull Parameter.RequiredSingle FORMULA_PARAM = Parameter.requiredRemainder("formula");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/CheckCommand.java b/src/main/java/de/prob2/jupyter/commands/CheckCommand.java
index 088dcce1a2bc911bac6c688cf24b3ea8a991358d..a20db9ea518bdc567efc0cbdfc021289bc8a37c1 100644
--- a/src/main/java/de/prob2/jupyter/commands/CheckCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/CheckCommand.java
@@ -22,9 +22,9 @@ import de.prob.statespace.Trace;
 import de.prob.unicode.UnicodeTranslator;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.UserErrorException;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -34,7 +34,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class CheckCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle WHAT_PARAM = new PositionalParameter.RequiredSingle("what");
+	private static final @NotNull Parameter.RequiredSingle WHAT_PARAM = Parameter.required("what");
 	
 	private static final @NotNull Map<@NotNull String, @NotNull Class<? extends AbstractTheoremElement>> CHILDREN_BASE_CLASS_MAP;
 	static {
diff --git a/src/main/java/de/prob2/jupyter/commands/ConstantsCommand.java b/src/main/java/de/prob2/jupyter/commands/ConstantsCommand.java
index 1dc90cce6f547ca0bc1dc8aad7109ce4fae3a343..0388125740feb74112eb864b4ee210a2dcde7295 100644
--- a/src/main/java/de/prob2/jupyter/commands/ConstantsCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/ConstantsCommand.java
@@ -12,9 +12,9 @@ import de.prob.statespace.Trace;
 import de.prob.statespace.Transition;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class ConstantsCommand implements Command {
-	private static final @NotNull PositionalParameter.OptionalRemainder PREDICATE_PARAM = new PositionalParameter.OptionalRemainder("predicate");
+	private static final @NotNull Parameter.OptionalSingle PREDICATE_PARAM = Parameter.optionalRemainder("predicate");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/DotCommand.java b/src/main/java/de/prob2/jupyter/commands/DotCommand.java
index 75e48d972e130cb4529689a6572ea5e5616075b8..9148588b82b129983318eff2e7c54a38a8e7a655 100644
--- a/src/main/java/de/prob2/jupyter/commands/DotCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/DotCommand.java
@@ -22,9 +22,9 @@ import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -35,8 +35,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class DotCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle COMMAND_PARAM = new PositionalParameter.RequiredSingle("command");
-	private static final @NotNull PositionalParameter.OptionalRemainder FORMULA_PARAM = new PositionalParameter.OptionalRemainder("formula");
+	private static final @NotNull Parameter.RequiredSingle COMMAND_PARAM = Parameter.required("command");
+	private static final @NotNull Parameter.OptionalSingle FORMULA_PARAM = Parameter.optionalRemainder("formula");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/EvalCommand.java b/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
index 30de5cd08f16111c5852bcaa566a1aa6006f0713..34c33999d0dd90c1198213fdb0bc15adfccf73c9 100644
--- a/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
@@ -9,9 +9,9 @@ import de.prob.animator.domainobjects.FormulaExpand;
 import de.prob.statespace.AnimationSelector;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class EvalCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder FORMULA_PARAM = new PositionalParameter.RequiredRemainder("formula");
+	private static final @NotNull Parameter.RequiredSingle FORMULA_PARAM = Parameter.requiredRemainder("formula");
 	
 	private final @NotNull Injector injector;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/ExecCommand.java b/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
index 931a5ca1f68275c869fe107db37736861c3b0eb7..5515f0fe2ce18778489f2be6e5b6ac46935d482f 100644
--- a/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/ExecCommand.java
@@ -14,9 +14,9 @@ import de.prob.statespace.Trace;
 import de.prob.statespace.Transition;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -26,8 +26,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class ExecCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle OPERATION_PARAM = new PositionalParameter.RequiredSingle("operation");
-	private static final @NotNull PositionalParameter.OptionalRemainder PREDICATE_PARAM = new PositionalParameter.OptionalRemainder("predicate");
+	private static final @NotNull Parameter.RequiredSingle OPERATION_PARAM = Parameter.required("operation");
+	private static final @NotNull Parameter.OptionalSingle PREDICATE_PARAM = Parameter.optionalRemainder("predicate");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/FindCommand.java b/src/main/java/de/prob2/jupyter/commands/FindCommand.java
index e1aae326b416ad06dd22743d485a0dc8d6d9a829..88aa529a7a5e22693b219eb4ad5f9fc88d7cdd7d 100644
--- a/src/main/java/de/prob2/jupyter/commands/FindCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/FindCommand.java
@@ -11,9 +11,9 @@ import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class FindCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder PREDICATE_PARAM = new PositionalParameter.RequiredRemainder("predicate");
+	private static final @NotNull Parameter.RequiredSingle PREDICATE_PARAM = Parameter.requiredRemainder("predicate");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/GotoCommand.java b/src/main/java/de/prob2/jupyter/commands/GotoCommand.java
index 78770f246d78b5e71501def594846a7ef1a2f265..798d0e30e60f7d826e6c653061908d51def99896 100644
--- a/src/main/java/de/prob2/jupyter/commands/GotoCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/GotoCommand.java
@@ -6,11 +6,10 @@ import com.google.inject.Inject;
 
 import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
-
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.UserErrorException;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -20,7 +19,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class GotoCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle INDEX_PARAM = new PositionalParameter.RequiredSingle("index");
+	private static final @NotNull Parameter.RequiredSingle INDEX_PARAM = Parameter.required("index");
 	
 	private final @NotNull AnimationSelector animationSelector;
 	
diff --git a/src/main/java/de/prob2/jupyter/commands/GroovyCommand.java b/src/main/java/de/prob2/jupyter/commands/GroovyCommand.java
index 43bfe4f67b5daa8dfb97fce8484c8bd5a0570202..083cef677e16b05fc9d75a544586428840cd4b93 100644
--- a/src/main/java/de/prob2/jupyter/commands/GroovyCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/GroovyCommand.java
@@ -10,11 +10,10 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 import de.prob.scripting.ScriptEngineProvider;
-
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -24,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class GroovyCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder EXPRESSION_PARAM = new PositionalParameter.RequiredRemainder("expression");
+	private static final @NotNull Parameter.RequiredSingle EXPRESSION_PARAM = Parameter.requiredRemainder("expression");
 	
 	private final @NotNull Injector injector;
 	private final @NotNull ScriptEngine groovyScriptEngine;
diff --git a/src/main/java/de/prob2/jupyter/commands/HelpCommand.java b/src/main/java/de/prob2/jupyter/commands/HelpCommand.java
index 487e63a9d8c672221912f1edf3c41074fc1da2de..57d4418d55042e66cdafde6cf6a605d976f76c3b 100644
--- a/src/main/java/de/prob2/jupyter/commands/HelpCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/HelpCommand.java
@@ -13,9 +13,9 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class HelpCommand implements Command {
-	private static final @NotNull PositionalParameter.OptionalSingle COMMAND_NAME_PARAM = new PositionalParameter.OptionalSingle("commandName");
+	private static final @NotNull Parameter.OptionalSingle COMMAND_NAME_PARAM = Parameter.optional("commandName");
 	
 	private static final @NotNull Map<@NotNull String, @NotNull List<@NotNull Class<? extends Command>>> COMMAND_CLASS_CATEGORIES;
 	static {
diff --git a/src/main/java/de/prob2/jupyter/commands/InitialiseCommand.java b/src/main/java/de/prob2/jupyter/commands/InitialiseCommand.java
index b7bf1555d896452bd6a7559bf3e6d7c3aa1c96fa..b93fc03a204377ecc3ae24fc867ec3467147ed5c 100644
--- a/src/main/java/de/prob2/jupyter/commands/InitialiseCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/InitialiseCommand.java
@@ -12,9 +12,9 @@ import de.prob.statespace.Trace;
 import de.prob.statespace.Transition;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class InitialiseCommand implements Command {
-	private static final @NotNull PositionalParameter.OptionalRemainder PREDICATE_PARAM = new PositionalParameter.OptionalRemainder("predicate");
+	private static final @NotNull Parameter.OptionalSingle PREDICATE_PARAM = Parameter.optionalRemainder("predicate");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/LetCommand.java b/src/main/java/de/prob2/jupyter/commands/LetCommand.java
index 4d7d6826188e007c080cd830d07e033c04c3fe71..24868dedbe9836bdad718fdeb66cea441c0c8a05 100644
--- a/src/main/java/de/prob2/jupyter/commands/LetCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/LetCommand.java
@@ -11,9 +11,9 @@ import de.prob.animator.domainobjects.FormulaExpand;
 import de.prob.statespace.AnimationSelector;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -23,8 +23,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class LetCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle NAME_PARAM = new PositionalParameter.RequiredSingle("name");
-	private static final @NotNull PositionalParameter.RequiredRemainder EXPRESSION_PARAM = new PositionalParameter.RequiredRemainder("expression");
+	private static final @NotNull Parameter.RequiredSingle NAME_PARAM = Parameter.required("name");
+	private static final @NotNull Parameter.RequiredSingle EXPRESSION_PARAM = Parameter.requiredRemainder("expression");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java
index cdb7b21f7955e3dac9f000b614690a50c20fdcc6..1a39922989576ab7b43621458866d096d9ea9dfa 100644
--- a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java
@@ -12,9 +12,9 @@ import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -24,8 +24,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class LoadCellCommand implements Command {
-	private static final @NotNull PositionalParameter.OptionalMultiple PREFS_PARAM = new PositionalParameter.OptionalMultiple("prefs");
-	private static final @NotNull PositionalParameter.RequiredRemainder CODE_PARAM = new PositionalParameter.RequiredRemainder("code");
+	private static final @NotNull Parameter.Multiple PREFS_PARAM = Parameter.optionalMultiple("prefs");
+	private static final @NotNull Parameter.RequiredSingle CODE_PARAM = Parameter.body("code");
 	
 	private final @NotNull ClassicalBFactory classicalBFactory;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java b/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java
index f3e5dd3f7b4b1cb05309f2aec436b59b142bce9b..d8980f521b0b8e344a0760b1ad982935af1e8b27 100644
--- a/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java
@@ -21,9 +21,9 @@ import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -38,8 +38,8 @@ import org.slf4j.LoggerFactory;
 public final class LoadFileCommand implements Command {
 	private static final @NotNull Logger LOGGER = LoggerFactory.getLogger(LoadFileCommand.class);
 	
-	private static final @NotNull PositionalParameter.RequiredSingle FILE_NAME_PARAM = new PositionalParameter.RequiredSingle("fileName");
-	private static final @NotNull PositionalParameter.OptionalMultiple PREFS_PARAM = new PositionalParameter.OptionalMultiple("prefs");
+	private static final @NotNull Parameter.RequiredSingle FILE_NAME_PARAM = Parameter.required("fileName");
+	private static final @NotNull Parameter.Multiple PREFS_PARAM = Parameter.optionalMultiple("prefs");
 	
 	private final @NotNull Injector injector;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/PrefCommand.java b/src/main/java/de/prob2/jupyter/commands/PrefCommand.java
index c416a838a81fe819bd7db25ff65f137351b5e6c4..2baac286b0be4cad5e1f5a4466d3f2abed3fc126 100644
--- a/src/main/java/de/prob2/jupyter/commands/PrefCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/PrefCommand.java
@@ -12,12 +12,11 @@ import de.prob.animator.command.GetCurrentPreferencesCommand;
 import de.prob.animator.command.GetPreferenceCommand;
 import de.prob.animator.command.SetPreferenceCommand;
 import de.prob.statespace.AnimationSelector;
-
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.UserErrorException;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -27,7 +26,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class PrefCommand implements Command {
-	private static final @NotNull PositionalParameter.OptionalMultiple PREFS_PARAM = new PositionalParameter.OptionalMultiple("prefs");
+	private static final @NotNull Parameter.Multiple PREFS_PARAM = Parameter.optionalMultiple("prefs");
 	
 	private final @NotNull AnimationSelector animationSelector;
 	
diff --git a/src/main/java/de/prob2/jupyter/commands/PrettyPrintCommand.java b/src/main/java/de/prob2/jupyter/commands/PrettyPrintCommand.java
index a6fe79afe337eb2df2017c68a935788e56e1584c..752d0058d47cf37f4e568bb0c6dd6b8f7ff52efe 100644
--- a/src/main/java/de/prob2/jupyter/commands/PrettyPrintCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/PrettyPrintCommand.java
@@ -10,9 +10,9 @@ import de.prob.animator.domainobjects.IEvalElement;
 import de.prob.statespace.AnimationSelector;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
 import io.github.spencerpark.jupyter.kernel.display.DisplayData;
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class PrettyPrintCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder PREDICATE_PARAM = new PositionalParameter.RequiredRemainder("predicate");
+	private static final @NotNull Parameter.RequiredSingle PREDICATE_PARAM = Parameter.requiredRemainder("predicate");
 	
 	private final AnimationSelector animationSelector;
 	
diff --git a/src/main/java/de/prob2/jupyter/commands/RenderCommand.java b/src/main/java/de/prob2/jupyter/commands/RenderCommand.java
index 22e4d2bae5e29ca65e3e985bb4e8afad387d19c0..cafd252f42c27c165a4f72c5259483c185b1e811 100644
--- a/src/main/java/de/prob2/jupyter/commands/RenderCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/RenderCommand.java
@@ -5,9 +5,9 @@ import java.util.Collections;
 import com.google.inject.Inject;
 
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
 import io.github.spencerpark.jupyter.kernel.display.DisplayData;
@@ -16,8 +16,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class RenderCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle MIME_TYPE_PARAM = new PositionalParameter.RequiredSingle("mimeType");
-	private static final @NotNull PositionalParameter.RequiredRemainder CONTENT_PARAM = new PositionalParameter.RequiredRemainder("content");
+	private static final @NotNull Parameter.RequiredSingle MIME_TYPE_PARAM = Parameter.required("mimeType");
+	private static final @NotNull Parameter.RequiredSingle CONTENT_PARAM = Parameter.body("content");
 	
 	@Inject
 	private RenderCommand() {
diff --git a/src/main/java/de/prob2/jupyter/commands/SolveCommand.java b/src/main/java/de/prob2/jupyter/commands/SolveCommand.java
index d172344c5970dfdf5b90ef8f7ecc68a3168c37a1..d8d3973ba9c77f0958b21899969e9110db832b35 100644
--- a/src/main/java/de/prob2/jupyter/commands/SolveCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/SolveCommand.java
@@ -13,12 +13,11 @@ import de.prob.animator.domainobjects.FormulaExpand;
 import de.prob.animator.domainobjects.IEvalElement;
 import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
-
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -29,8 +28,8 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class SolveCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle SOLVER_PARAM = new PositionalParameter.RequiredSingle("solver");
-	private static final @NotNull PositionalParameter.RequiredRemainder PREDICATE_PARAM = new PositionalParameter.RequiredRemainder("predicate");
+	private static final @NotNull Parameter.RequiredSingle SOLVER_PARAM = Parameter.required("solver");
+	private static final @NotNull Parameter.RequiredSingle PREDICATE_PARAM = Parameter.requiredRemainder("predicate");
 	
 	private static final @NotNull Map<@NotNull String, CbcSolveCommand.@NotNull Solvers> SOLVERS = Arrays.stream(CbcSolveCommand.Solvers.values())
 		.collect(Collectors.toMap(s -> s.name().toLowerCase(), s -> s));
diff --git a/src/main/java/de/prob2/jupyter/commands/TableCommand.java b/src/main/java/de/prob2/jupyter/commands/TableCommand.java
index c43bb8c83564671f15739665d34de1f74e9afd0b..74553f1ff311a956cdebca27389c28c7e113994a 100644
--- a/src/main/java/de/prob2/jupyter/commands/TableCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/TableCommand.java
@@ -18,9 +18,9 @@ import de.prob.statespace.Trace;
 import de.prob.unicode.UnicodeTranslator;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class TableCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder EXPRESSION_PARAM = new PositionalParameter.RequiredRemainder("expression");
+	private static final @NotNull Parameter.RequiredSingle EXPRESSION_PARAM = Parameter.requiredRemainder("expression");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/TimeCommand.java b/src/main/java/de/prob2/jupyter/commands/TimeCommand.java
index aacd756df8ccb57d3805be0d45567c96c983018b..844936bd5073437f76b521f3ffd66b7d7b54728c 100644
--- a/src/main/java/de/prob2/jupyter/commands/TimeCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/TimeCommand.java
@@ -9,9 +9,9 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class TimeCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder COMMAND_AND_ARGS_PARAM = new PositionalParameter.RequiredRemainder("commandAndArgs");
+	private static final @NotNull Parameter.RequiredSingle COMMAND_AND_ARGS_PARAM = Parameter.requiredRemainder("commandAndArgs");
 	
 	private final @NotNull Injector injector;
 	
diff --git a/src/main/java/de/prob2/jupyter/commands/TypeCommand.java b/src/main/java/de/prob2/jupyter/commands/TypeCommand.java
index d8a9236dfc393379584514b24f3ad5b4c240e302..67ed0c3f05e5a2e6ba7452a5e8860c905c72dadd 100644
--- a/src/main/java/de/prob2/jupyter/commands/TypeCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/TypeCommand.java
@@ -13,9 +13,9 @@ import de.prob.statespace.AnimationSelector;
 import de.prob.statespace.Trace;
 import de.prob2.jupyter.Command;
 import de.prob2.jupyter.CommandUtils;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class TypeCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredRemainder FORMULA_PARAM = new PositionalParameter.RequiredRemainder("formula");
+	private static final @NotNull Parameter.RequiredSingle FORMULA_PARAM = Parameter.requiredRemainder("formula");
 	
 	private final @NotNull Provider<@NotNull ProBKernel> kernelProvider;
 	private final @NotNull AnimationSelector animationSelector;
diff --git a/src/main/java/de/prob2/jupyter/commands/UnletCommand.java b/src/main/java/de/prob2/jupyter/commands/UnletCommand.java
index f3da397f8590a5a8e877664b66b728a4226cf140..c2bb02b109ba40fdad89348901491fe046cbf956 100644
--- a/src/main/java/de/prob2/jupyter/commands/UnletCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/UnletCommand.java
@@ -7,9 +7,9 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 
 import de.prob2.jupyter.Command;
+import de.prob2.jupyter.Parameter;
 import de.prob2.jupyter.Parameters;
 import de.prob2.jupyter.ParsedArguments;
-import de.prob2.jupyter.PositionalParameter;
 import de.prob2.jupyter.ProBKernel;
 import de.prob2.jupyter.UserErrorException;
 
@@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public final class UnletCommand implements Command {
-	private static final @NotNull PositionalParameter.RequiredSingle NAME_PARAM = new PositionalParameter.RequiredSingle("name");
+	private static final @NotNull Parameter.RequiredSingle NAME_PARAM = Parameter.required("name");
 	
 	private final @NotNull Injector injector;