How to represent the MediaStream constraints object in Elm?

Here are the different shapes of the MediaStream constraints:

{ audio: true, video: true }

Or

{
    audio: true,
    video: { width: 1280, height: 720 }
}

Or

{
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}

Or

{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}

Or

{ audio: true, video: { facingMode: "user" } }

{ audio: true, video: { facingMode: { exact: "environment" } } }

It looks trivial but … not in Elm.
I feel that I should use a flat record to represent this data on the Elm side:

type alias Constraints =
    { audio : Bool
    , video : Bool
    , videoWidth : Maybe Int
    , videoHeight: Maybe Int
    , videoWidthMin: Maybe Int
    , ...  
   }

But I feel also that I could just implement several constraints environments:

type alias Constraints =
    { audio : Bool
    , video : Bool
   }

type alias VideoSizeConstraints =
    { audio : Bool
    , video : Bool
    , videoWidth :  Int
    , videoHeight:  Int
   }

type alias VideoMinSizeConstraints =
    { audio : Bool
    , video : Bool
    , videoWidthMin: Int
    , videoHeightMin: Int
   }

Then pattern match on the type alias to parse it and send the data through a port and call navigator.mediaDevices.getUserMedia() on the javascript side.
It is definitely much more code to write but I prefer this than one record full of Maybe values.

What do you think of?

2 Likes

What do you think about modeling the video bounds as a union type? So…

type Bounds
    = Unspecified
    | Minimum Int
    | Exactly Int
    | Within Int Int Int

and then in your decoder:

bounds : Decoder Bounds
bounds =
    oneOf
        [ map Exactly int
        , map Minimum (field "min" int)
        , map3 Within
            (field "min" int)
            (field "ideal" int)
            (field "max" int)
        , succeed Unspecified
        ]

(note: I’m not familiar with all the shapes of this specification, so I’m just going off of the ones you posted.) The decoder may need a lot more work than this, but modeling with union types can simplify these aliases quite a bit:

type alias Video =
    { height : Bounds
    , width : Bounds
    }
7 Likes

@ggerico you’re definitely thinking the right way by identifying those booleans and maybes as signs you can better structure the data :+1:

:100: to @brian’s suggestion to model with union types. The fact that you describe the problem as “data can be in one of n shapes” strongly suggests that union types are the solution.

Elm gives you a rich set of tools to model constraints and dependencies between fields.

For example, it sounds like you have to request either audio, or video (with constraints) or both (with video constraints) but requesting neither is a bit pointless. That could be modeled (using Brian’s Bounds type) as:

type MediaStream
  = AudioOnly
  | VideoOnly Bounds Bounds
  | BothAudioAndVideo Bounds Bounds

I’ve written an article on modeling with union types if you’re interested in exploring the concept further

4 Likes