Skip to content

Instantly share code, notes, and snippets.

@juliendehos
Last active July 11, 2025 19:48
Show Gist options
  • Save juliendehos/c4817ef6ca8f1a9a04fe8d1994653b1e to your computer and use it in GitHub Desktop.
Save juliendehos/c4817ef6ca8f1a9a04fe8d1994653b1e to your computer and use it in GitHub Desktop.
implementing optional parameters in three.hs
// In three.js, many functions have optional parameters.
// For example, the color constructor can take 0, 1, 2 or 3 parameters:
const c1 = new THREE.Color(0xff00ff);
const c2 = new THREE.Color();
const c3 = new THREE.Color(1,0,0);
const c4 = new THREE.Color('blue');
const c5 = new THREE.Color(new THREE.Color('green'));
// Here, the function is a bit complicated because the first parameter can have different types.
// In many other functions, each parameter has only one type.
-- Intuitively, we would write something like this, in tree.hs:
newColor :: (Maybe Double, Maybe Double, Maybe Double) -> THREE.Three Color
newColor args = THREE.new Color "Color" args
-- so we could construct a color like:
newColor (Just 0, Nothing, Nothing)
-- but this seems to produce the JS code: new THREE.Color(0, [], [])
-- whereas we want: new THREE.Color(0) or new THREE.Color(0,1,1)
-- Instead, we can pattern match the Maybe values:
newColor :: (MakeArgs a, ToJSVal a) => (Maybe a, Maybe Double, Maybe Double) -> THREE.Three Color
newColor (Just r, Just g, Just b) = THREE.new Color "Color" (r, g, b)
newColor (Just r, Just g, _) = THREE.new Color "Color" (r, g)
newColor (Just x, _, _) = THREE.new Color "Color" x
newColor _ = THREE.new Color "Color" ()
test :: THREE.Three ()
test = do
c1 <- newColor (Just 0xff00ff :: Maybe Int, Nothing, Nothing)
c2 <- newColor (Nothing :: Maybe (), Nothing, Nothing)
c3 <- newColor (Just 1 :: Maybe Int, Just 0, Just 0)
c4 <- newColor (Just "blue" :: Maybe JSString, Nothing, Nothing)
c5 <- newColor (Just c4, Nothing, Nothing)
pure ()
-- We can also "override" the function:
newColorInt :: Int -> THREE.Three Color
newColorInt = THREE.new Color "Color"
newColorUnit :: () -> THREE.Three Color
newColorUnit = THREE.new Color "Color"
newColorRGB :: (Double, Double, Double) -> THREE.Three Color
newColorRGB = THREE.new Color "Color"
newColorString :: JSString -> THREE.Three Color
newColorString = THREE.new Color "Color"
newColorColor :: Color -> THREE.Three Color
newColorColor = THREE.new Color "Color"
test :: THREE.Three ()
test = do
c1 <- newColorInt 0xff00ff
c2 <- newColorUnit ()
c3 <- newColorRGB (1, 0, 0)
c4 <- newColorString "blue"
c5 <- newColorColor =<< newColorString "green"
pure ()
-- Or define a datatype, for the parameters:
data ColorArgs
= ColorArgsUnit
| ColorArgsInt Int
| ColorArgsString JSString
| ColorArgsColor Color
| ColorArgsRGB Double Double Double
newColor :: ColorArgs -> THREE.Three Color
newColor = \case
ColorArgsUnit -> THREE.new Color "Color" ()
ColorArgsInt x -> THREE.new Color "Color" x
ColorArgsString x -> THREE.new Color "Color" x
ColorArgsColor x -> THREE.new Color "Color" x
ColorArgsRGB r g b -> THREE.new Color "Color" (r, g, b)
test :: THREE.Three ()
test = do
c1 <- newColor (ColorArgsInt 0xff00ff)
c2 <- newColor ColorArgsUnit
c3 <- newColor (ColorArgsRGB 1 0 0)
c4 <- newColor (ColorArgsString "blue")
c5 <- newColor . ColorArgsColor =<< newColor (ColorArgsString "green")
pure ()
-- Or use a typeclass:
class ColorArgs t
instance ColorArgs ()
instance ColorArgs Int
instance ColorArgs JSString
instance ColorArgs Color
instance ColorArgs (Double, Double, Double)
newColor :: (MakeArgs args, ColorArgs args) => args -> THREE.Three Color
newColor = THREE.new Color "Color"
test :: THREE.Three ()
test = do
c1 <- newColor (0xff00ff::Int)
c2 <- newColor ()
c3 <- newColor (1::Double, 0::Double, 0::Double)
c4 <- newColor ("blue"::JSString)
c5 <- newColor =<< newColor ("green"::JSString)
pure ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment