content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE KindSignatures #-}
  
  {-# LANGUAGE NoIncoherentInstances #-}
  {-# LANGUAGE NoMonomorphismRestriction #-}
  {-# LANGUAGE NoUndecidableInstances #-}
  
  module Vivid.SCServer.Types (
  
     -- Types
       Synth(..)
  
     -- Functions
     , shrinkSynthArgs
  
     -- Classes
     , IsNode(..)
     , SynthOrNodeId
     , IsGroup
     ) where
  
  import Data.Int
  import GHC.TypeLits
  import Vivid.SC.Server.Types
  import Vivid.SynthDef.TypesafeArgs
  
  
  -- | This enforces type safety of the arguments -- e.g. if you have a synthdef
  -- 
  --   >> let x = sd (3 ::I "foo") bar
  --   >> s <- synth x ()
  -- 
  --   Then this won't typecheck (because "bar" isn't an argument to x):
  -- 
  --   >> set s (4 ::I "bar")
  -- 
  --   Note that if you don't want this type safety, you can e.g.
  -- 
  --   >> Synth n <- synth foo ()
  --   >> setG n (0.1 ::I "vol")
  -- 
  --   Or:
  -- 
  --   >> ns <- mapM (flip synth ()) [foo, bar, baz]
  --   >> map (setG (0::I "asdf") . unSynth) ns
  -- 
  --   Or:
  -- 
  --   >> n <- synthG foo ()
  -- 
  --   (You also may want to look at 'shrinkSynthArgs' if you want to construct a list
  --   which has synthdefs or nodes of different types)
  newtype Synth (args :: [Symbol]) = Synth { _unSynth :: NodeId }
   deriving (Show, Read, Eq, Ord)
  
  -- | So let's say you have a node:
  -- 
  --   > foo :: Synth '["amp", "freq", "phase"]
  -- 
  --   and you want to add it to a list of nodes:
  -- 
  --   > ns :: [Synth '["freq", "phase"]]
  -- 
  --   If you don't plan on setting the \"amp\" argument, you can \"shrink\" to
  --   the compatible arguments:
  -- 
  --   > ns' = shrinkSynthArgs foo : ns
  -- 
  --   (The same thing exists for SynthDefs -- 'Vivid.SynthDef.shrinkSDArgs')
  shrinkSynthArgs :: (Subset new old) => Synth old -> Synth new
  shrinkSynthArgs (Synth nId) = Synth nId
  
  class IsNode a where getNodeId :: a -> NodeId
  
  instance IsNode NodeId where getNodeId n = n
  instance IsNode (Synth a) where getNodeId (Synth n) = n
  instance IsNode Group where getNodeId (Group n) = n
  instance IsNode ParGroup where getNodeId (ParGroup n) = n
  
  -- | For gradually-typed 'free'
  class IsNode a => SynthOrNodeId a
  instance SynthOrNodeId (Synth x)
  instance SynthOrNodeId NodeId
  
  -- | 'Group' and 'ParGroup'
  class IsNode g => IsGroup g
  instance IsGroup Group
  instance IsGroup ParGroup
  