Commit shinji-based arrow system from whole weeks ago

This commit is contained in:
Aly 2021-03-14 22:47:13 -07:00
parent eadb95472b
commit 9a81322ff8
14 changed files with 416 additions and 223 deletions

130
.gitignore vendored
View file

@ -1,4 +1,128 @@
.bloop/*
.metals/* # Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all,visualstudiocode,metals,sbt,scala
# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all,visualstudiocode,metals,sbt,scala
### JetBrains+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Metals ###
.metals/
.bloop/
project/**/metals.sbt
### SBT ###
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
.bsp
### Scala ###
*.class
*.log
### VisualStudioCode ###
.vscode/* .vscode/*
out/* !.vscode/tasks.json
!.vscode/launch.json
*.code-workspace
### VisualStudioCode Patch ###
# Ignore all local history of files
.ionide
# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,visualstudiocode,metals,sbt,scala

9
build.sbt Normal file
View file

@ -0,0 +1,9 @@
lazy val core = (project in file("core")).settings(
organization := "tf.bug",
name := "fancade",
version := "2.0.0-SNAPSHOT",
scalaVersion := "2.13.4",
libraryDependencies ++= Seq(
"tf.bug" %% "shinji" % "0.1.3",
),
)

121
build.sc
View file

@ -1,121 +0,0 @@
import mill._, scalalib._, publish._
import $ivy.`io.github.davidgregory084::mill-tpolecat:0.1.4`
import io.github.davidgregory084.TpolecatModule
object core extends TpolecatModule with PublishModule {
override def scalaVersion = "2.13.3"
override def artifactName = "fancade-core"
override def publishVersion = "2.0.0"
override def pomSettings = PomSettings(
description = "A library for representing and transforming Fancade scripts",
organization = "tf.bug",
url = "https://gitdab.com/s/fancadescala",
licenses = Seq(License.MIT),
versionControl = VersionControl(
browsableRepository = Some("https://gitdab.com/s/fancadescala"),
connection = Some(VersionControlConnection.gitGit("gitdab.com", "s/fancadescala.git")),
developerConnection = Some(VersionControlConnection.gitSsh("ssh.gitdab.com", ":s/fancadescala.git", username = Some("git"))),
tag = None,
),
developers = Seq(
Developer("sorenbug", "Soren", "https://gitdab.com/s"),
),
)
override def ivyDeps = Agg(
ivy"org.typelevel::cats-core:2.2.0",
ivy"io.higherkindness::droste-core:0.8.0",
ivy"org.scala-graph::graph-core:1.13.2",
)
}
object tagless extends TpolecatModule with PublishModule {
override def scalaVersion = "2.13.3"
override def artifactName = "fancade-tagless"
override def publishVersion = "2.0.0"
override def pomSettings = PomSettings(
description = "A tagless DSL for building Fancade scripts",
organization = "tf.bug",
url = "https://gitdab.com/s/fancadescala",
licenses = Seq(License.MIT),
versionControl = VersionControl(
browsableRepository = Some("https://gitdab.com/s/fancadescala"),
connection = Some(VersionControlConnection.gitGit("gitdab.com", "s/fancadescala.git")),
developerConnection = Some(VersionControlConnection.gitSsh("ssh.gitdab.com", ":s/fancadescala.git", username = Some("git"))),
tag = None,
),
developers = Seq(
Developer("sorenbug", "Soren", "https://gitdab.com/s"),
),
)
override def ivyDeps = Agg(
ivy"org.typelevel::cats-tagless-macros:0.11",
)
override def moduleDeps = Seq(core)
override def scalacPluginIvyDeps = Agg(ivy"org.typelevel:::kind-projector:0.11.0", ivy"com.olegpy::better-monadic-for:0.3.1")
}
object scodec extends TpolecatModule with PublishModule {
override def scalaVersion = "2.13.3"
override def artifactName = "fancade-scodec"
override def publishVersion = "2.0.0"
override def pomSettings = PomSettings(
description = "A library for reading and writing the Fancade level format",
organization = "tf.bug",
url = "https://gitdab.com/s/fancadescala",
licenses = Seq(License.MIT),
versionControl = VersionControl(
browsableRepository = Some("https://gitdab.com/s/fancadescala"),
connection = Some(VersionControlConnection.gitGit("gitdab.com", "s/fancadescala.git")),
developerConnection = Some(VersionControlConnection.gitSsh("ssh.gitdab.com", ":s/fancadescala.git", username = Some("git"))),
tag = None,
),
developers = Seq(
Developer("sorenbug", "Soren", "https://gitdab.com/s"),
),
)
override def moduleDeps = Seq(core)
}
object recipe extends TpolecatModule with PublishModule {
override def scalaVersion = "2.13.3"
override def artifactName = "fancade-recipe"
override def publishVersion = "2.0.0"
override def pomSettings = PomSettings(
description = "An application that creates Fancade levels from other data",
organization = "tf.bug",
url = "https://gitdab.com/s/fancadescala",
licenses = Seq(License.MIT),
versionControl = VersionControl(
browsableRepository = Some("https://gitdab.com/s/fancadescala"),
connection = Some(VersionControlConnection.gitGit("gitdab.com", "s/fancadescala.git")),
developerConnection = Some(VersionControlConnection.gitSsh("ssh.gitdab.com", ":s/fancadescala.git", username = Some("git"))),
tag = None,
),
developers = Seq(
Developer("sorenbug", "Soren", "https://gitdab.com/s"),
),
)
override def ivyDeps = Agg(
ivy"org.typelevel::cats-effect:2.2.0",
ivy"co.fs2::fs2-core:2.4.3",
ivy"co.fs2::fs2-io:2.4.3",
ivy"com.monovore::decline:1.3.0",
ivy"com.monovore::decline-effect:1.3.0",
)
override def moduleDeps = Seq(tagless, scodec)
override def scalacPluginIvyDeps = Agg(ivy"org.typelevel:::kind-projector:0.11.0", ivy"com.olegpy::better-monadic-for:0.3.1")
}

View file

@ -0,0 +1,214 @@
package tf.bug.fancade
import tf.bug.shinji.{Arrow, Cartesian, Monoidal}
object Fanscript {
sealed trait Pin
sealed trait Number extends Pin
sealed trait Bool extends Pin
sealed trait Vector extends Pin
sealed trait Rotation extends Pin
sealed trait Obj extends Pin
sealed trait Constraint extends Pin
sealed trait Pull extends Pin
sealed trait T extends Pin
sealed trait ×[A <: Pin, B <: Pin] extends Pin
sealed trait [A <: Pin, B <: Pin]
case class Id[A <: Pin]() extends (A A)
case class AndThen[A <: Pin, B <: Pin, C <: Pin](f: A B, g: B C) extends (A C)
case class Swap[A <: Pin, B <: Pin]() extends ((A × B) (B × A))
case class Deunit[A <: Pin]() extends (A (T × A))
case class Unit[A <: Pin]() extends ((T × A) A)
case class Associate[A <: Pin, B <: Pin, C <: Pin]() extends (((A × B) × C) (A × (B × C)))
case class Duplicate[A <: Pin]() extends (A (A × A))
case class Terminate[A <: Pin]() extends (A T)
case class Bimap[A <: Pin, B <: Pin, C <: Pin, D <: Pin](f: A B, g: C D) extends ((A × C) (B × D))
object Builtin {
case class NumberValue(number: Float) extends (T Number)
case class VectorValue(x: Float, y: Float, z: Float) extends (T Vector)
case class RotationValue(x: Float, y: Float, z: Float) extends (T Rotation)
case object TrueValue extends (T Bool)
case object FalseValue extends (T Bool)
case object InspectNumber extends ((Number × Pull) Pull)
case object InspectVector extends ((Vector × Pull) Pull)
case object InspectRotation extends ((Rotation × Pull) Pull)
case object InspectTruth extends ((Bool × Pull) Pull)
case object InspectObject extends ((Obj × Pull) Pull)
case object Win extends (Pull Pull)
case object Lose extends (Pull Pull)
case object SetScore extends ((Number × Pull) Pull)
case object SetCamera extends ((Vector × Rotation × Number × Pull) Pull)
case object SetLight extends ((Vector × Rotation × Pull) Pull)
case object ScreenSize extends (T (Number × Number))
case object Accelerometer extends (T Vector)
case object CurrentFrame extends (T Number)
case object GetPosition extends (Obj (Vector × Rotation))
case object SetPosition extends ((Obj × Vector × Rotation × Pull) Pull)
case object Raycast extends ((Vector × Vector) (Bool × Vector × Obj))
case object GetSize extends (Obj (Vector × Vector))
case object SetVisible extends ((Obj × Bool × Pull) Pull)
case object CreateObject extends ((Obj × Pull) (Obj × Pull))
case object DestroyObject extends ((Obj × Pull) Pull)
case class PlaySound(loop: Boolean, sample: SoundSample) extends ((Number × Number × Pull) (Number × Pull))
case object StopSound extends ((Number × Pull) Pull)
case object VolumePitch extends ((Number × Number × Number × Pull) Pull)
case object AddForce extends ((Obj × Vector × Vector × Vector × Vector × Pull) Pull)
case object GetVelocity extends (Obj (Vector × Vector))
case object SetVelocity extends ((Obj × Vector × Vector × Pull) Pull)
case object SetLocked extends ((Obj × Vector × Vector × Pull) Pull)
case object SetMass extends ((Obj × Number × Pull) Pull)
case object SetFriction extends ((Obj × Number × Pull) Pull)
case object SetBounciness extends ((Obj × Number × Pull) Pull)
case object SetGravity extends ((Vector × Pull) Pull)
case object AddConstraint extends ((Obj × Obj × Vector × Pull) (Constraint × Pull))
case object LinearLimits extends ((Constraint × Vector × Vector × Pull) Pull)
case object AngularLimits extends ((Constraint × Vector × Vector × Pull) Pull)
case object LinearSpring extends ((Constraint × Vector × Vector × Pull) Pull)
case object AngularSpring extends ((Constraint × Vector × Vector × Pull) Pull)
case object LinearMotor extends ((Constraint × Vector × Vector × Pull) Pull)
case object AngularMotor extends ((Constraint × Vector × Vector × Pull) Pull)
case object If extends ((Bool × Pull × Pull × Pull) Pull)
case object OnPlay extends ((Pull × Pull) Pull)
case object LateUpdate extends ((Pull × Pull) Pull)
case object BoxArt extends ((Pull × Pull) Pull)
case class OnTouch(event: TouchEvent, finger: TouchFinger) extends ((Pull × Pull) (Number × Number × Pull))
case object OnSwipe extends ((Pull × Pull) (Vector × Pull))
case object OnCollision extends ((Obj × Pull × Pull) (Obj × Number × Vector × Pull))
case object Loop extends ((Number × Number × Pull × Pull) (Number × Pull))
case object Negate extends (Number Number)
case object Inverse extends (Rotation Rotation)
case object AddNumbers extends ((Number × Number) Number)
case object AddVectors extends ((Vector × Vector) Vector)
case object SubtractNumbers extends ((Number × Number) Number)
case object SubtractVectors extends ((Vector × Vector) Vector)
case object Multiply extends ((Number × Number) Number)
case object Scale extends ((Vector × Number) Vector)
case object Rotate extends ((Vector × Rotation) Vector)
case object Combine extends ((Rotation × Rotation) Rotation)
case object Divide extends ((Number × Number) Number)
case object Power extends ((Number × Number) Number)
case object EqualNumbers extends ((Number × Number) Bool)
case object EqualVectors extends ((Vector × Vector) Bool)
case object EqualObjects extends ((Obj × Obj) Bool)
case object EqualTruths extends ((Bool × Bool) Bool)
case object LessThan extends ((Number × Number) Bool)
case object GreaterThan extends ((Number × Number) Bool)
case object And extends ((Bool × Bool) Bool)
case object Or extends ((Bool × Bool) Bool)
case object Not extends (Bool Bool)
case object Random extends ((Number × Number) Number)
case object RandomSeed extends ((Number × Pull) Pull)
case object Min extends ((Number × Number) Number)
case object Max extends ((Number × Number) Number)
case object Sin extends (Number Number)
case object Cos extends (Number Number)
case object Round extends (Number Number)
case object Floor extends (Number Number)
case object Ceiling extends (Number Number)
case object Absolute extends (Number Number)
case object Modulo extends ((Number × Number) Number)
case object Logarithm extends ((Number × Number) Number)
case object BreakVector extends (Vector (Number × Number × Number))
case object MakeVector extends ((Number × Number × Number) Vector)
case object Normalize extends (Vector Vector)
case object DotProduct extends ((Vector × Vector) Vector)
case object CrossProduct extends ((Vector × Vector) Vector)
case object BreakRotation extends (Rotation (Number × Number × Number))
case object MakeRotation extends ((Number × Number × Number) Rotation)
case object Distance extends ((Vector × Vector) Number)
case object LERP extends ((Rotation × Rotation × Number) Rotation)
case object AxisAngle extends ((Vector × Number) Rotation)
case object ScreenToWorld extends ((Number × Number) (Vector × Vector))
case object WorldToScreen extends (Vector (Number × Number))
case object LineVsPlane extends ((Vector × Vector × Vector × Vector) Vector)
case object LookRotation extends ((Vector × Vector) Rotation)
case class GetNumber(name: String) extends (T Number)
case class GetObject(name: String) extends (T Obj)
case class GetVector(name: String) extends (T Vector)
case class GetRotation(name: String) extends (T Rotation)
case class GetTruth(name: String) extends (T Bool)
case class GetConstraint(name: String) extends (T Constraint)
case class SetNumber(name: String) extends ((Number × Pull) Pull)
case class SetObject(name: String) extends ((Obj × Pull) Pull)
case class SetVector(name: String) extends ((Vector × Pull) Pull)
case class SetRotation(name: String) extends ((Rotation × Pull) Pull)
case class SetTruth(name: String) extends ((Bool × Pull) Pull)
case class SetConstraint(name: String) extends ((Constraint × Pull) Pull)
case object SetNumberRef extends ((Number × Number × Pull) Pull)
case object SetObjectRef extends ((Obj × Obj × Pull) Pull)
case object SetVectorRef extends ((Vector × Vector × Pull) Pull)
case object SetRotationRef extends ((Rotation × Rotation × Pull) Pull)
case object SetTruthRef extends ((Bool × Bool × Pull) Pull)
case object SetConstraintRef extends ((Constraint × Constraint × Pull) Pull)
case object ListNumber extends ((Number × Number) Number)
case object ListObject extends ((Obj × Number) Obj)
case object ListVector extends ((Vector × Number) Vector)
case object ListRotation extends ((Rotation × Number) Rotation)
case object ListTruth extends ((Bool × Number) Bool)
case object ListConstraint extends ((Constraint × Number) Constraint)
case object Increment extends ((Number × Pull) Pull)
case object Decrement extends ((Number × Pull) Pull)
}
implicit val arr: Cartesian[Pin, , T, ×] with Arrow[Pin, , T, ×, ] = new Cartesian[Pin, , T, ×] with Arrow[Pin, , T, ×, ] {
override def id[A <: Pin]: A A =
Id[A]()
override def duplicate[A <: Pin]: A (A × A) =
Duplicate[A]()
override def terminate[A <: Pin]: A T =
Terminate[A]()
override def swap[A <: Pin, B <: Pin]: (A × B) (B × A) =
Swap()
override def lift[A <: Pin, B <: Pin](f: A B): A B =
f
override val strongProfunctorCategory: Monoidal[Pin, , T, ×] = this
override def first[A <: Pin, B <: Pin, C <: Pin]: (A B) ((A × C) (B × C)) =
Bimap[A, B, C, C](_, id[C])
override def second[A <: Pin, B <: Pin, C <: Pin]: (A B) ((C × A) (C × B)) =
(f: A B) => {
val firstf: (A × C) (B × C) = first[A, B, C](f)
AndThen(Swap(), AndThen(firstf, Swap()))
}
override def associateRight[A <: Pin, B <: Pin, C <: Pin]: ((A × B) × C) (A × (B × C)) =
Associate[A, B, C]()
override def unitorLeft[A <: Pin]: (T × A) A =
Unit()
override def deunitorLeft[A <: Pin]: A (T × A) =
Deunit()
override def compose[A <: Pin, B <: Pin, C <: Pin](f: B C, g: A B): A C =
AndThen(g, f)
override def andThen[A <: Pin, B <: Pin, C <: Pin](f: A B, g: B C): A C =
AndThen(f, g)
override def bimap[A <: Pin, B <: Pin, C <: Pin, D <: Pin](f: A C, g: B D): (A × B) (C × D) =
Bimap(f, g)
}
}

View file

@ -0,0 +1,23 @@
package tf.bug.fancade
object Main {
def main(args: Array[String]): Unit = {
import Fanscript._
import tf.bug.shinji.syntax.all._
val sum: (Number × Number × Pull × Number × Pull) (Pull × Number × Number × Pull) = {
val getSum: T Number = Builtin.GetNumber("Sum")
val addToSum: (Number × Pull) Pull = {
val add: (Number × Number) Number = Builtin.AddNumbers
val sumAsFirstInput: (T × Number) (Number × Number) = getSum.first[Number]
val branch: Number (T × Number) = deunitL
val justSecondInput: Number (Number × Number) = branch >>> sumAsFirstInput
val addSum: Number Number = add.ldimap(justSecondInput)
val storeSum: (Number × Pull) Pull = Builtin.SetNumber("Sum")
storeSum.ldimap(arr.lbimap(addSum))
}
}
}
}

View file

@ -0,0 +1,23 @@
package tf.bug.fancade
sealed trait SoundSample
object SoundSample {
case object Chirp extends SoundSample
case object Scrape extends SoundSample
case object Squeek extends SoundSample
case object Engine extends SoundSample
case object Button extends SoundSample
case object Ball extends SoundSample
case object Piano extends SoundSample
case object Marimba extends SoundSample
case object Pad extends SoundSample
case object Beep extends SoundSample
case object Plop extends SoundSample
case object Flop extends SoundSample
case object Splash extends SoundSample
case object Boom extends SoundSample
case object Hit extends SoundSample
case object Clang extends SoundSample
case object Jump extends SoundSample
}

View file

@ -0,0 +1,9 @@
package tf.bug.fancade
sealed trait TouchEvent
object TouchEvent {
case object Touching extends TouchEvent
case object Begins extends TouchEvent
case object Ends extends TouchEvent
}

View file

@ -0,0 +1,9 @@
package tf.bug.fancade
sealed trait TouchFinger
object TouchFinger {
case object First extends TouchFinger
case object Second extends TouchFinger
case object Third extends TouchFinger
}

View file

@ -1,7 +0,0 @@
package tf.bug.fancade
object Foo {
def main(args: Array[String]): Unit = {
println("hi")
}
}

1
project/build.properties Normal file
View file

@ -0,0 +1 @@
sbt.version=1.4.7

1
project/plugins.sbt Normal file
View file

@ -0,0 +1 @@

View file

@ -1,55 +0,0 @@
package tf.bug.fancade.recipe
import cats.implicits._
import com.monovore.decline.effect.CommandIOApp
import cats.effect.{ExitCode, IO}
import com.monovore.decline.Opts
import java.nio.file.Path
import com.monovore.decline.Argument
object Main extends CommandIOApp(
name = "fancade-recipe",
header = "Fancade recipe creator and format helper",
version = "2.0.0",
) {
sealed trait InFormat
object InFormat {
implicit val arg: Argument[InFormat] = Argument.fromMap("input-format", Map(
"json" -> FancadeStackJson,
"fcl" -> FancadeLisp,
))
}
sealed trait OutFormat
object OutFormat {
implicit val arg: Argument[OutFormat] = Argument.fromMap("output-format", Map(
"json" -> FancadeStackJson,
"txt" -> Recipe,
"bin" -> Level,
))
}
case object FancadeStackJson extends InFormat with OutFormat
case object FancadeLisp extends InFormat
case object Recipe extends OutFormat
case object Level extends OutFormat
case class Compile(in: InFormat, inFile: Path, out: OutFormat, outFile: Path)
val compileOpts = {
val inFormatOpts = Opts.argument[InFormat]("input-format")
val inFileOpts = Opts.argument[Path]("input-path")
val outFormatOpts = Opts.argument[OutFormat]("output-format")
val outFileOpts = Opts.argument[Path]("output-path")
Opts.subcommand("compile", "Converts between script formats") {
(inFormatOpts, inFileOpts, outFormatOpts, outFileOpts).mapN(Compile)
}
}
override def main: Opts[IO[ExitCode]] = compileOpts.map {
case Compile(in, _, out, _) =>
IO(println(s"hello $in $out")).as(ExitCode.Success)
}
}

View file

@ -1,5 +0,0 @@
package tf.bug.fancade.scodec
object Baz {
}

View file

@ -1,32 +0,0 @@
package tf.bug.fancade.tagless
trait Fanscript[F[_]] {
def unit: F[Unit]
def lift(b: Boolean): F[Boolean]
def lift(f: Float): F[Float]
def lift(v: Vector): F[Vector]
def lift(r: Rotation): F[Rotation]
def lift(o: Obj): F[Obj]
def win(stopProgram: Boolean)(andThen: () => F[Unit]): F[Unit]
def lose(stopProgram: Boolean)(andThen: () => F[Unit]): F[Unit]
def setScore(scoreMode: ScoreMode)(score: F[Float], andThen: () => F[Unit]): F[Unit]
def setCamera(position: F[Vector], rotation: F[Rotation], distance: F[Float], andThen: () => F[Unit]): F[Unit]
def setLight(position: F[Vector], rotation: F[Rotation], andThen: () => F[Unit]): F[Unit]
def screenSize: F[ScreenSize]
def accelerometer: F[Vector]
def getPosition(obj: F[Obj]): F[PosRot]
def setPosition(obj: F[Obj], position: F[Vector], rotation: F[Rotation], andThen: () => F[Unit]): F[Unit]
def rayCast(from: F[Vector], to: F[Vector]): F[RaycastResult]
def getSize(obj: F[Obj]): F[ObjSize]
def setVisible(obj: F[Obj], visible: F[Boolean], andThen: () => F[Unit]): F[Unit]
def createObj(original: F[Obj], andThen: (F[Obj]) => F[Unit]): F[Unit]
def destroyObj(obj: F[Obj], andThen: () => F[Unit]): F[Unit]
def playSound(loop: Boolean, sample: Sample)(volume: F[Float], pitch: F[Float], andThen: (F[Float]) => F[Unit]): F[Unit]
def stopSound(channel: F[Float], andThen: () => F[Unit]): F[Unit]
def volumePitch(channel: F[Float], volume: F[Float], pitch: F[Float], andThen: () => F[Unit]): F[Unit]
}