关于类型系统的Internet上的圣战继续受到广泛的神话的困扰,即动态类型系统本质上更适合于对“开放世界”的主题领域进行建模。通常的论点是:静态类型化的目的是尽可能准确地捕获所有实体,但是在现实世界中这很不方便。实际系统应松散耦合,并尽可能减少与数据表示的关系,因此动态类型可导致整个系统更稳定。

这个故事听起来很有说服力,但事实并非如此。这种推理的错误在于,静态类型并非旨在 “对世界进行分类”或确定系统中每个值的结构。现实情况是,静态类型系统使您可以准确地指定组件应了解其输入数据的结构的多少,反之,也可以指定其不知道的数目。在实践中,静态类型系统以部分已知的结构完美地处理数据,并且能够确保应用程序逻辑不会意外地对数据承担过多的责任。
关于类型的两个谎言
, . , , /r/programming:
[…], . , , «» , , - .
, , . JSON , . , . […] « » , .
, , , , . , - , , ? , , .
Hacker News :
, , pickle.load()
?
, , , . , , .
, , . , , : . , ; , .
,
: , , , , ! , . , .
. , , . (payload), . , - , JSON EDN.
, , :
{
"event_type": "signup",
"timestamp": "2020-01-19T05:37:09Z",
"data": {
"user": {
"id": 42,
"name": "Alyssa",
"email": "alyssa@example.com"
}
}
}
signup
- . , . JavaScript, :
const handleEvent = ({ event_type, data }) => {
switch (event_type) {
case 'login':
/* ... */
break
case 'signup':
sendEmail(data.user.email, `Welcome to Blockchain Emporium, ${data.user.name}!`)
break
}
}
, Haskell? , Haskell, , , :
data Event = Login LoginPayload | Signup SignupPayload
data LoginPayload = LoginPayload { userId :: Int }
data SignupPayload = SignupPayload
{ userId :: Int
, userName :: Text
, userEmail :: Text
}
instance FromJSON Event where
parseJSON = withObject "Event" \obj -> do
eventType <- obj .: "event_type"
case eventType of
"login" -> Login <$> (obj .: "data")
"signup" -> Signup <$> (obj .: "signup")
_ -> fail $ "unknown event_type: " <> eventType
instance FromJSON LoginPayload where { ... }
instance FromJSON SignupPayload where { ... }
handleEvent :: JSON.Value -> IO ()
handleEvent payload = case fromJSON payload of
Success (Login LoginPayload { userId }) -> {- ... -}
Success (Signup SignupPayload { userName, userEmail }) ->
sendEmail userEmail $ "Welcome to Blockchain Emporium, " <> userName <> "!"
Error message -> fail $ "could not parse event: " <> message
, (, , ). . , Reddit, , Haskell , ! Event, . , ? .
, JavaScript . , switch
. , JavaScript . , .
, , . Event
, , handleEvent
. JavaScript, , :
const handleEvent = ({ event_type, data }) => {
switch (event_type) {
/* ... */
default:
throw new Error(`unknown event_type: ${event_type}`)
}
}
, . , , . , , Haskell:
handleEvent :: JSON.Value -> IO ()
handleEvent payload = case fromJSON payload of
{- ... -}
Error _ -> pure ()
- «, », , . , ( ) . , ! , .
: Event
Haskell « », , . , , . , , , .
, , , :
Haskell, . , , timestamp
, . , , , , , !
, , Haskell userId
SignupPayload
, . , (, , userId
), ; , , , .
, , (shotgun parsing), , .
, , , , , . , , . , , , , , , .
JavaScript , Haskell: , JSON event_type
«» signup
data.user.name
data.user.email
. ! , JavaScript , . , ; , , - .
, , , , .
, , , - -. , , . , . JavaScript :
const handleEvent = (payload) => {
const signedPayload = { ...payload, signature: signature(payload) }
retransmitEvent(signedPayload)
}
( signature JSON), , . , ?
, : , . Haskell:
handleEvent :: JSON.Value -> IO ()
handleEvent (Object payload) = do
let signedPayload = Map.insert "signature" (signature payload) payload
retransmitEvent signedPayload
handleEvent payload = fail $ "event payload was not an object " <> show payload
, , JSON.Value
. Event
— JSON , , .
: , JSON- ( -), - JSON, - . , , , , . .
, , Haskell, JavaScript! JavaScript handleEvent
( JSON ), , , , spread- …
:
> { ..."payload", signature: "sig" }
{0: "p", 1: "a", 2: "y", 3: "l", 4: "o", 5: "a", 6: "d", signature: "sig"}
, . , . «» JSON Object
, . -, , .
, . , API, , , UUID
. «, » , API Haskell UUID
:
type UserId = UUID
, Reddit, , ! API , UUID, , . UUID , , , ! , ?
, — . , — . , UserId
— :
newtype UserId = UserId Text
deriving (Eq, FromJSON, ToJSON)
, UUID
, UserId
, , Text
. ( , ), UserId
— FromJSON
. , , UserId
— UserId
ToJSON
. : .
, , . UserId
, UserId
.
1, FromJSON
UserId
, , , fromJSON
. , - . , , … UserId
. , , ( , , , ).
— , . , , , .
-
, , , , , . pickle.load()
Python? , , Python. pickle.dump()
, pickle.load()
.
, , pickle.load()
, — , pickle.dump()
. , , . , , , .
, JSON, , pickle Python Python , . ? , , pickle.load()
. , :
def load_value(f):
val = pickle.load(f)
# - `val`
, val
, , , , , - . - - , , pickle.load(f)
, , val
!
, , , val
val.foo()
, . Java, val
— , :
interface Foo extends Serializable {
String foo();
}
, pickle.load()
Java:
static <T extends Serializable> Optional<T> load(InputStream in, Class<? extends T> cls);
, , pickle.load()
, Class<T>
. Serializable.class
, . : , -, - , ! , , JSON-.
Haskell? — serialise, API, , Java, . API , Haskell JSON, aeson, , , JSON Haskell — - - , -.
, , pickle.load()
, . , . - , , , , . , (, REPL ), , .
. , , , , - , «» , , . , , , , , .
. , , , ( ). , , «» .
: ,
: . , , , , , .
, , . -, - (nominal typing). , , , , . , . , .
, , , , . , , -, , ( ).
. JavaScript Clojure - , - , . ( ) .
, , - , , . ( ) , (nominal typing). , ; (boilerplate).
, , . , , , . , :
, , , , . ( ), : , .
, , - . , , , . , .
Python, , , , TypeScript, , . , . - Clojure — , - — , - Clojure, - .
, : TypeScript, Flow, PureScript, Elm, OCaml Reason, . — Haskell, ; Haskell ( , ) .
, Haskell , ? , ; Haskell, , . Haskell, , . , Haskell, , , . ( , , Haskell!)
: , , , . , , , . — , , , , . .