Karya, built on 2023-08-29T07:47:28 (patch 7a412d5d6ba4968ca4155ef276a062ccdeb9109a)
Safe HaskellSafe-Inferred

Derive.Sig

Description

Functions to help define call signatures.

This module, along with the Typecheck.Typecheck class, define a little DSL to express function signatures. Check existing calls for examples.

Argument passing, in an effort to be flexible, got a bit complicated. Each Arg has a name and a possible default. So right off there are three ways to provide an argument:

  1. Pass it explicitly.
  2. If it is omitted, or _ is passed explicitly, it will be sought in the dynamic environ, under the name <call_name>-<arg_name>. E.g. given a call generator "name" $ \args -> call (required "arg1") ... then name-arg1 = 42 | call _ will get 42. Note that it uses the call name, and not the symbol it happens to bound to in this scope. This is because, while you may bind different kinds of trills to tr depending on the needs of the score, the two kinds of trills may have different arguments with different meanings.
  3. If it's omitted, and not in the dynamic environ, the default will be used, provided there is one.

In addition, an arg may be a DeriveT.VPControlRef or DeriveT.ControlRef, which introduces yet another way to provide the value. An argument required_control "c" will pass a DeriveT.Ref. Technically it's then up to the call to decide what to do with it, but it will likely look it up at its chosen point in time, which means you can provide the value by providing a c control track or binding it explicitly e.g. %c = .5 | call. TODO: out of date, there is no longer Typecheck ControlRef, they are resolved by Typecheck.from_val

  • To further complicate the matter, the control arg may itself have a default, to relieve the caller from always having to provide that control. So an argument control "c" 0.5 or an explicitly provided control val call %c,.5 will default to 0.5 if the c control is not in scope.

Since the arg defaulting and control defaulting are orthogonal, they can be combined:

  1. Pass it explicitly with a default: call %c,.5. This is either the value of %c or 0.5.
  2. Pass it via the dynamic environ: call-arg1 = %c,.5 | call. This is the same as the above, only the argument is provided implicitly.
  3. Fall back on the built-in default: control "c" 0.5 and then just call.

I originally envisioned the dynamic environ passing scheme to be a way to default certain arguments within the context of a track, to be used in a relatively ad-hoc way in specific parts of the score (e.g. all trills within this section of melody default to minor thirds), is not limited to numeric types, and is constant in time. A control, however, is intended to capture musical parameters that change in time over the course of the piece, and is numeric or a pitch. So while dynamic environ args are forced to be specific to a certain call by prepending the call's name, control names should generally have more general and abstract names.

On the subject of controls, controls (and numeric vals in general) have another layer of complexity since they carry types. For example, here's a gloriously complicated argument: defaulted "speed" (typed_control "tremolo-speed" 10 ScoreT.Real). This argument defaults to %tremolo-speed,10s. If it's not given, it will have the value 10s. If the %tremolo-speed control is in scope but untyped, its values will be interpreted as RealTime. If it's in scope and typed (e.g. with a tremolo-speed:t track), then its values will be interpreted as ScoreTime.

Another wrinkle in argument passing is that, in addition to being required, which has no default, or being defaulted, which has a default, they can be defaulted with a default of Nothing. This passes the argument as a Maybe a instead of a and lets the call distinguish whether an argument was provided or not. This is for arguments which are defaulted but need a more complicated defaulting strategy than simply a constant.

Synopsis

Documentation

data Parser a Source #

Instances

Instances details
Applicative Parser Source # 
Instance details

Defined in Derive.Sig

Methods

pure :: a -> Parser a #

(<*>) :: Parser (a -> b) -> Parser a -> Parser b #

liftA2 :: (a -> b -> c) -> Parser a -> Parser b -> Parser c #

(*>) :: Parser a -> Parser b -> Parser b #

(<*) :: Parser a -> Parser b -> Parser a #

Functor Parser Source # 
Instance details

Defined in Derive.Sig

Methods

fmap :: (a -> b) -> Parser a -> Parser b #

(<$) :: a -> Parser b -> Parser a #

type Generator y d = PassedArgs y -> Deriver d Source #

Similar to GeneratorF, but leaves the PassedArgs prev val type free. This is important for val calls, which use Tagged.

type Transformer y d = PassedArgs y -> Deriver d -> Deriver d Source #

data Arg Source #

Instances

Instances details
Pretty.Pretty Arg Source # 
Instance details

Defined in Derive.Sig

check Source #

Arguments

:: (a -> Maybe Text)

return Just error if there's a problem

-> Parser a 
-> Parser a 

Annotate a parser with a check on its value.

parse :: Taggable d => Parser a -> PassedArgs d -> Deriver (Either Error a) Source #

Run a parser against the current derive state.

data Dummy Source #

Now that the default can be any ToVal and may be a different type than the parser, it can get ambiguous, especially if it's a Nothing or []. Since all that matters is (show_val . to_val), and show_val of Nothing or [] is always _ or (list), the type doesn't matter. I could use Int, but I'll use this to explicitly mark that it doesn't matter.

Instances

Instances details
Typecheck.ToVal Dummy Source # 
Instance details

Defined in Derive.Sig

parsers

no_args :: Parser () Source #

Parser for nullary calls. Either use this with call and callt, or use call0 and call0t as a shortcut.

required :: forall a. Typecheck.Typecheck a => ArgName -> Doc.Doc -> Parser a Source #

The argument is required to be present, and have the right type.

defaulted :: forall a deflt. (Typecheck.Typecheck a, Typecheck.ToVal deflt) => ArgName -> deflt -> Doc.Doc -> Parser a Source #

The argument is not required to be present, but if it is, it has to have either the right type or be VNotGiven.

defaulted_env :: forall a deflt. (Typecheck.Typecheck a, Typecheck.ToVal deflt) => ArgName -> EnvironDefault -> deflt -> Doc.Doc -> Parser a Source #

defaulted_env_quoted :: forall a. Typecheck.Typecheck a => ArgName -> EnvironDefault -> DeriveT.Quoted -> Doc.Doc -> Parser a Source #

The defaulted value can be a DeriveT.Quoted, which will be evaluated if needed.

maybe_defaulted :: (Typecheck.Typecheck a, Typecheck.ToVal deflt) => ArgName -> Maybe deflt -> Doc.Doc -> Parser a Source #

This is either required or defaulted, depending on if there's a default value. Useful for making call variants with instrument-specific defaults.

environ Source #

Arguments

:: forall a deflt. (Typecheck.Typecheck a, Typecheck.ToVal deflt) 
=> ArgName 
-> EnvironDefault

None doesn't make any sense, but, well, don't pass that then.

-> deflt 
-> Doc.Doc 
-> Parser a 

This is an argument which is not actually parsed from the argument list. Instead it's looked up it the environ according to the normal defaulting rules. So it's like defaulted except there is no positional argument.

Of course, the call could just look in the environ itself, but this way it's uniform and automatically documented.

environ_key :: (Typecheck.Typecheck a, Typecheck.ToVal deflt) => Key -> deflt -> Doc.Doc -> Parser a Source #

A shortcut for an unprefixed environ key.

environ_quoted :: forall a. Typecheck.Typecheck a => ArgName -> EnvironDefault -> DeriveT.Quoted -> Doc.Doc -> Parser a Source #

This is like environ, but the default is a DeriveT.Quoted, which will be evaluated if needed.

required_environ :: forall a. Typecheck.Typecheck a => ArgName -> EnvironDefault -> Doc.Doc -> Parser a Source #

This is like environ, but without a default.

optional :: forall a. (Typecheck.Typecheck a, ShowVal.ShowVal a) => ArgName -> a -> Doc.Doc -> Parser a Source #

This is like defaulted, but if the argument is the wrong type return the default instead of failing. It's mostly useful with many or many1, where you can distinguish the arguments by type.

many :: forall a. Typecheck.Typecheck a => ArgName -> Doc.Doc -> Parser [a] Source #

Collect the rest of the arguments.

many_vals :: ArgName -> Doc.Doc -> Parser [DeriveT.Val] Source #

many specialized to Vals, to avoid a type annotation.

many1 :: forall a. Typecheck.Typecheck a => ArgName -> Doc.Doc -> Parser (NonEmpty a) Source #

Collect the rest of the arguments, but there must be at least one.

many_pairs :: forall a b. (Typecheck.Typecheck a, Typecheck.Typecheck b) => ArgName -> Doc.Doc -> Parser [(a, b)] Source #

Collect the rest of the arguments, but expect a even number of them and pair them up.

many1_pairs :: forall a b. (Typecheck.Typecheck a, Typecheck.Typecheck b) => ArgName -> Doc.Doc -> Parser (NonEmpty (a, b)) Source #

Like many_pairs, but require at least one pair.

required_vals :: [ArgDoc] -> Parser [Arg] Source #

Require one Val for each ArgDoc given, but otherwise do no typechecking.

defaults

data EnvironDefault Source #

This configures how an argument looks for a default in the environ.

Constructors

None

Don't default from environ at all.

Prefixed

Look for callname-argname.

Unprefixed

Look for argname. This is useful for generic parameters that should configure many calls simultaneously.

Both

First look for a prefixed key, then for an unprefixed one.

call

call :: Taggable y => Parser a -> (a -> Generator y d) -> WithArgDoc (Generator y d) Source #

call_sub :: Taggable y => Parser a -> (a -> Generator y d) -> WithArgDoc (Generator y d) Source #

call0 :: Taggable y => Generator y d -> WithArgDoc (Generator y d) Source #

Specialization of call for 0 arguments.

callt :: Taggable y => Parser a -> (a -> Transformer y d) -> WithArgDoc (Transformer y d) Source #

call0t :: Taggable y => Transformer y d -> WithArgDoc (Transformer y d) Source #

Specialization of callt for 0 arguments.

Orphan instances