Skip to content

Instantly share code, notes, and snippets.

@pwm
Last active August 24, 2021 07:39
Show Gist options
  • Save pwm/fcafeaa031553d29166d369f4e0d58a8 to your computer and use it in GitHub Desktop.
Save pwm/fcafeaa031553d29166d369f4e0d58a8 to your computer and use it in GitHub Desktop.
"Make illegal states unrepresentable" (Yaron Minsky)
{-# OPTIONS_GHC -Wall #-}
-- An example of representing a connection
-- Types
data ConnInfo = ConnInfo
{ connState :: ConnState
, server :: String
} deriving (Show)
data ConnState
= C1 Connecting
| C2 Connected
| C3 Disconnected
deriving (Show)
data Connecting = Connecting
{ whenInitiated :: Int
} deriving (Show)
data Connected = Connected
{ sessionId :: String
, lastPing :: Maybe Int
} deriving (Show)
data Disconnected = Disconnected
{ whenDisconnected :: Int
} deriving (Show)
-- Code
getSessionId :: ConnInfo -> String
getSessionId ci = getSid $ connState ci where
getSid (C1 _) = "Connecting..."
getSid (C2 c) = sessionId c
getSid (C3 _) = "Disconnected"
connected :: ConnInfo
connected = ConnInfo
(C2 (Connected "session_id_1234" Nothing))
"api.foo.app"
main :: IO ()
main = print $ getSessionId connected -- "session_id_1234"
<?php declare(strict_types=1);
// An example of representing a connection
// Types
class ConnInfo {
/** @var ConnState */
private $connState;
/** @var string */
private $server;
public function __construct(ConnState $connState, string $server) {
$this->connState = $connState;
$this->server = $server;
}
public function connState(): ConnState {
return $this->connState;
}
public function server(): string {
return $this->server;
}
}
class ConnState {
/** @var string */
private $tag;
/** @var Connecting | Connected | Disconnected */
private $variant;
private const C1 = 'C1';
private const C2 = 'C2';
private const C3 = 'C3';
public static function C1(Connecting $connecting): self {
return new self(self::C1, $connecting);
}
public static function C2(Connected $connected): self {
return new self(self::C2, $connected);
}
public static function C3(Disconnected $disconnected): self {
return new self(self::C3, $disconnected);
}
public function match(
Closure $connecting,
Closure $connected,
Closure $disconnected
) {
switch ($this->tag) {
case self::C1: return $connecting($this->variant);
case self::C2: return $connected($this->variant);
case self::C3: return $disconnected($this->variant);
}
}
private function __construct(string $tag, $variant) {
$this->tag = $tag;
$this->variant = $variant;
}
}
class Connecting {
/** @var int */
private $whenInitiated;
public function __construct(int $whenInitiated) {
$this->whenInitiated = $whenInitiated;
}
public function whenInitiated(): int {
return $this->whenInitiated;
}
}
class Connected {
/** @var string */
private $sessionId;
/** @var null|int */
private $lastPing;
public function __construct(string $sessionId, ?int $lastPing) {
$this->sessionId = $sessionId;
$this->lastPing = $lastPing;
}
public function sessionId(): string {
return $this->sessionId;
}
public function lastPing(): ?int {
return $this->lastPing;
}
}
class Disconnected {
/** @var int */
private $whenDisconnected;
public function __construct(int $whenDisconnected) {
$this->whenDisconnected = $whenDisconnected;
}
public function whenDisconnected(): int {
return $this->whenDisconnected;
}
}
// Code
function getSessionId(ConnInfo $ci): string {
return $ci->connState()->match(
function (Connecting $_): string { return 'Connecting...'; },
function (Connected $c): string { return $c->sessionId(); },
function (Disconnected $_): string { return 'Disconnected'; }
);
}
$connected = new ConnInfo(
ConnState::C2(new Connected('session_id_1234', null)),
'api.foo.app'
);
echo getSessionId($connected); // session_id_1234
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment