web-dev-qa-db-ja.com

現実の世界で「強力な」型システムを使用している、たとえば、大規模なWebアプリの場合はどうでしょうか。

これは非常に広範で、あいまいで、おそらく哲学的な質問であることを知っています。ある程度、質問の中で最も重要なキーワード-「強力な」型システム-それ自体は ill-defined です。だから、私が何を意味するのか説明してみましょう。

質問の全体的なコンテキスト

Ruby on Rails)で非常に大規模なWebアプリを構築しており、スタックについては一般的に満足しています。必要に応じて、ものを本当に速く出荷することができます-"ビジネス"ケースの90%で機能し、約10%のエッジケースをあまり心配する必要はありません。一方、コードレビューとテストカバレッジの助けを借りて、ゆっくりと慎重に、すべての拠点をカバーするようにしてください。このような綿密な調査と安全に値する状況でのみです。

しかし、チームが成長するにつれて、スタックに組み込まれた「セーフティネット」が欠如していることに不快に感じ始めました。

私たちは最近、JavaでいくつかのネイティブAndroid=開発を開始しました。そして、コンパイルされた/静的/厳密に型指定された言語によって提供される安全性を(喜んで)思い出しました。

  • スペルが間違っている変数、誤ったデータ型、誤った関数呼び出し、および些細なエラーのホストは、IDE自体によって捕捉されます。IDEコンパイラに入力し、プログラムの「正確さ」の特定の側面を確認します。
  • 関数のシグネチャを変更する必要がありますか?簡単です。 compiler + IDEは、すべての呼び出しサイトを見つけるのに役立ちます。
  • 特定の例外が常に処理されるようにする必要がありますか?あなたの救助の例外を確認しました。

現在、これらの安全機能には長所がありますが、短所もよく知っています。さらに、「定型の重い」Javaの世界では。したがって、私はJavaの代わりに、人々が最近取り組んでいる現代の「強く型付けされた」多くの言語を調べ始めました。例:Scala、Rust、Haskellなど。最も興味深いのは、型システムと静的/コンパイル時チェックの能力です。

さて、質問

より大きなアプリケーションでこれらの強力な型システムと静的/コンパイル時機能を使用するにはどうすればよいですか?

たとえば、これらの強力な機能への標準の「hello world」のような導入をどのように進めればよいでしょうか?リッチタイプシステムを使用してビジネスドメインの問題をモデル化するものですか。タイプシステムは、30,000 LOC +ゾーンにいるときに役立ちますか、それとも妨げになりますか?システムが弱く型付けされた外部の世界とやり取りすると、これらの型システム(およびコンパイル時チェック)によって提供されるセーフティネットはどうなりますか。 JSONまたはXML API、さまざまなデータストア、ユーザー入力などを介して.

29
Saurabh Nanda

現時点では時間が足りないので、短い回答をさせていただきますが、現在2つの大きなプロジェクト(Haskellで100.000 LOCを超える)に取り組んでいます- flowbox.io および luna-lang.org 。私たちは、プログラミング言語のバックエンド、コンパイラー、さらにはwebGLベースのGUIを含むすべての部分にHaskellを使用しています。強力な型システムと「依存型」のような機構があなたを導き、他の言語で知られている負担と面倒からあなたを救うことができることを認めなければなりません。私たちは型を非常に広範囲に使用しており、コンパイル時にチェックできるすべてのものはそうされています。実際、過去3年間の開発中に、ランタイムエラーやスタックオーバーフローが発生することはありませんでした(これは本当に驚くべきことです)。唯一のエラーは、プログラマーが行った明らかな論理エラーです。多くの人は、Haskellでコンパイルされたものは機能するだけで、いつか顔に当たらないことを確信していると言っています。これはほとんどの状況に当てはまり、言語をよく理解していて、回避すべきことがわかっている場合(実装されていない型クラスメソッドなど)、型システムから安全に大きな利益を得ることができます。

質問の最初の部分への回答:次のようないくつかの優れたブログを読んで、これらの強力な型システム機能について学ぶことができます。

実際、他にもたくさんのニースブログがあります( planet Haskell など)。とにかく、高度な型システムを本当に理解するための最良の方法は、有用なオープンソースライブラリを開発することです。私たちは(FlowboxとNew Byte Orderで)多くのライブラリをリリースしています(Hackageで見つけることができます)。したがって、何を開発するべきかわからない場合は、いつでも私たちのプロジェクトに参加できます。必要です(メールは luna-lang.org で入手できます)。

34
danilo2

ええ、弱い型と強い型はかなり漠然と定義されています。さらに、「強い型付け」の一般的な使用法に最も近いのは、型をキャストするのを困難にするものを参照することであるため、さらに強力な型システムを説明することはできません。持ち運べる体重が30ポンド未満だと弱いと言っているようなもので、もっと持ち上げることができる人は皆、「強い」という同じカテゴリーに分類されます。

だから私は定義を好む:

  • 弱く型付けされたシステムは型を使用して、特定のこと(間違いなど)を防ぐ
  • 強く型付けされたシステムは型を使用してあなたのために何かをする

あなたのために物事を行うとはどういう意味ですか?さて、サーバントフレームワークでの画像変換APIの記述を調べてみましょう(Haskellでは、実際にそれを知る必要はありませんが、わかります...)

{-# LANGUAGE
    TypeOperators,
    DataKinds
    #-}

import Codec.Picture
import Data.Proxy
import Network.Wai.Handler.Warp (run)
import Servant
import Servant.JuicyPixels

main :: IO ()
main = run 8001 conversion

これは、サーバントパッケージとサーバントへのJuicyPixelsプラグインを含むいくつかのモジュールが必要であり、プログラムの主なエントリポイントは、ポート8001でWarpバックエンドを使用するサーバーとして「変換」関数を実行することです。言語ビットを無視します。

conversion :: Application
conversion = serve (Proxy :: Proxy ConversionApi) handler

これは、変換関数がAPIがタイプ 'ConversionApi'と一致する必要があり、リクエストが関数handlerによって処理されるサーバーであることを示しています。

type ConversionApi
     = ReqBody '[BMP, GIF, JPEG 50, PNG, TIFF, RADIANCE] DynamicImage
    :> Post '[BMP, GIF, JPEG 50, PNG, TIFF, RADIANCE] DynamicImage

これはConvesionApiタイプを指定しています。リスト '[BMP、GIF、JPEG 50、PNG、TIFF、RADIANCE]で指定された着信コンテンツタイプを受け入れ、それらをDynamicImageとして処理し、同じ範囲のコンテンツに変換されたDynamicImageを返す必要があることを示しています。タイプ。 :>が何を意味するかを正確に心配する必要はありません。今のところそれを幸せな魔法だと考えてください。

したがって、私の好ましい定義を考えると、弱く型付けされたシステムは、次のようなことを保証できます。

  • 間違った発信コンテンツタイプを返さない
  • 受信したリクエストを間違ったコンテンツタイプとして解析しない
  • サーバーがより複雑な場合、不正なURIを作成することはできませんが、実際にはリンクを含むHTMLページを返していません(そして、タイプによってリンクできないことが保証されます)。
  • 本当に野心的な弱い型付けシステムでは、すべての着信および発信コンテンツタイプを徹底的に処理していることを確認して、型が制約だけでなく仕様ドキュメントとしても機能できるようにすることもできます。

上記の定義を考えると、すべての高尚な目標ですが、厳密に型付けされたシステムとして認定するには十分ではありません。そして、この仕様に従うコードを実際に作成する際のhardの部分に到達する必要があります。本当にstrong型システムでは、次のように書きます:

handler = return

これで完了です。それだけですこれ以上記述するコードはありません。これは完全に動作するWebサーバーです(私が見逃したタイプミスを法として)。タイプは、定義してインポートしたタイプとパッケージ(技術的にはモジュール)からWebサーバーを作成するために必要なすべてのものをコンパイラーに伝えました。

では、これを主要なアプリケーションスケールでどのように学習するのでしょうか。まあ、それは実際に小規模なアプリケーションでそれらを使用することと大差ありません。絶対的な型は、それらに関連して書かれたコードの量を気にしません。

実行時の型検査は、おそらく回避したいと思うものです。これにより、型を単純化するのではなく、型がプロジェクトをより複雑に処理できるようになるため、メリットが大幅に削減されます。

そのため、ほとんどの場合、タイプで物事をモデル化する練習の問題です。物事をモデル化する(または物事を一般的に構築する)2つの主な方法は、ボトムアップとトップダウンです。トップダウンは、最高レベルの操作から始まり、モデルを構築するときに、モデリングを後で延期する部分があります。ボトムアップモデリングとは、基本機能から始めるのと同じように、基本操作から始めて、プロジェクトの操作を完全に取り込むまで、より大きなモデルを構築することを意味します。ボトムアップの方がより具体的であり、ビルドが速くなる可能性がありますが、トップダウンは、実際にどのように動作する必要があるかについて、より低レベルのモデルに通知する方がよい場合があります。

型とは、プログラムが文字通り数学にどのように関連するかであり、そのため、プログラムがどれほど複雑になるか、またはそれらについて「学ぶ」ことができる点に実際の上限はありません。高レベルの大学コース以外のリソースのほとんどすべてが、特定の言語で型がどのように機能するかに専念しているため、それも決定する必要があります。

私が提供できる限り、型は次のように階層化できます。

  • 非常に弱い型付け、[] + {}が定義されているJavaScriptのようなもの
  • Pythonのように弱く型付けされており、[] + {}は実行できませんが、試行するまでチェックされません
  • CまたはJavaのように弱く型付けされており、[] + {}は実行できませんが、コンパイル時にチェックされますが、より高度な型機能はありません
  • C++テンプレートのメタプログラミングなど、弱く型付けされた型と強く型付けされた型の境界と、型がプロパティのみを適用する単純なHaskellコードをまたぐ。
  • 上記のように、型が処理を行う、より複雑なHaskellプログラムのように、完全に強く型付けされている
  • AgdaやIdrisのように非常に強く型付けされており、型と値が相互作用して互いに制約し合う場合があります。これは型システムと同じくらい強力であり、型システムでのプログラミングは、プログラムが何をするかについて数学的な証明を書くことと同じです。注:Agdaでのコーディングはそうではありません文字通り数学的な証明を書くこと、タイプは数学的な理論であり、それらのタイプを持つ関数はそれらの理論を証明する建設的な例です。

一般的に、このリストを下に行くほど、タイプが実行できることが多くなりますが、最下部では、成層圏に上昇し、空気が少し薄くなっています-パッケージエコシステムははるかに小さく、関連するライブラリを見つけるよりも、自分でもっと書く必要があります。大規模なプログラムを作成するのに十分な型システムを実際に理解する必要があるため、下に行くにつれてエントリへの障壁も高くなります。

17

私はScalaで書かれた大きなプラットフォームのコアチームに取り組み始めたところです。 Scalatra、Play、Slickなどの成功したオープンソースアプリケーションを調べて、動的データ形式とのやり取りに関する詳細な質問のいくつかをどのように処理するかを確認できます。

Scalaの強力なタイピングについて私たちが見つけた大きな利点の1つは、ユーザー教育にあります。コアチームは、意思決定を行い、タイプシステムでそれらの決定を実施することができます。設計原則にあまり慣れていないため、システムとやり取りする必要があり、コンパイラが修正し、コアチームはプルリクエストの内容を常に修正しているわけではありません。これは、大規模なシステムではhugeの利点です。

もちろん、すべての設計原則を型システムで適用できるわけではありませんが、型システムが強力であればあるほど、コンパイラーで実行できる設計原則が増えます。

また、ユーザーにとって使いやすいものにすることもできます。多くの場合、彼らは通常のコレクションまたはケースクラスを操作しているだけであり、ネットワークトランスポートの必要に応じて、JSONまたは自動的に何かに変換しています。

強力なタイピングは、無害化された入力と無害化された入力などを区別するのにも役立ち、セキュリティに役立ちます。

厳密な型指定は、型だけをテストする一連のテストを必要とせずに、テストを実際の動作に集中させるのにも役立ちます。これにより、テストがより楽しく、より集中し、したがってより効果的になります。

主な欠点は、言語と言語パラダイムに慣れていないことであり、それは時間とともに修正可能です。それ以外にも、努力する価値は十分にあることがわかりました。

10
Karl Bielefeldt

直接的な回答ではありませんが(私はまだhaskellで+30.000 LOCコードベースに取り組んでいないため:( ..))、ぜひチェックしてください https://www.fpcomplete.com/business/resources/case-studies / 実際の業界設定でのhaskellの多くのケーススタディが特徴です。

もう1つの優れた記事はIMVUです。これは、彼らの経験がhaskellに変化することを説明しています http://engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell/

大規模なアプリケーションでの個人的な経験から、タイプシステムはveryを使用すると非常に役立ちます。物事をリファクタリングすることになると、真の力は本当に明白です。つまり、メンテナンスなどは、それほど気になりません。

一度にかなり多くの質問をしているので、私が推奨するリソースへのリンクをいくつかダンプします。

閉会の辞として、外の世界への対応に関してはいくつかの方法で行われます。 Aeson はJSON、 Esqueleto はSQLなど、タイプセーフを保証するライブラリがあります。

8
Tehnix

私が見たこと:

私はいくつかの大きなRuby Webアプリケーション(Rails)、1つの大きなHaskell Webアプリケーション、およびいくつかの小さなアプリケーションを使用してきました。その経験から、Haskellアプリケーションでの作業は多くのことを言わなければなりません。 Railsメンテナンスや学習曲線の低下などの点でより簡単です。これらの利点は両方ともHaskellの型システムと関数型プログラミングスタイルによるものだと思います。しかし、多くの場合とは異なり、型コントラクトの「静的」部分は、動的コントラクトを使用するときに得られるメリットがまだあるという点で、非常に便利です。

私が信じていること

Haskellプロジェクトがより優れたメンテナンス特性を実現するのに役立つと思われる主な機能のいくつかを提供するのに役立つ Contracts Ruby という素敵なパッケージがあります。コントラクトRubyは実行時にチェックを実行するので、高度なテスト収束と組み合わせると最適ですが、それでも、インライン注釈と型注釈の使用と同じ意図と表現の表現を提供しますHaskellなどの言語。

質問の答え

上記の質問に答えるために、Haskellや他の高度な型システムの言語に慣れることができる場所はたくさんあります。ただし、完全に正直に言うと、これらのドキュメントのソースはそれ自体は優れていますが、Ruby、Python、Java =と他のそのような言語。いずれの場合でも、 Real World Haskell は古くなっていますが、依然として優れたリソースです。

カテゴリー理論

Haskellを選択すると、カテゴリー理論について論じている大量の文献に出くわします。 IMHOカテゴリ理論は有用ですが、必須ではありません。 Haskellコミュニティで普及していることを考えると、タイプの賛否両論をカテゴリー理論の実用性についての感情と簡単に融合させることができます。それらが2つの異なるものであることを覚えておくと役立ちます。つまり、カテゴリ理論によって導かれる実装は、静的だけでなく動的に型付けされた言語でも実行できます(型システムが提供する利点を法として)。高度な型システムは一般にカテゴリー理論に拘束されておらず、カテゴリー理論は型システムに拘束されていません。

タイプの詳細

型を使用したプログラミングとその中のテクニック(楽しいので非常に迅速に行われます)についてさらに学習すると、型システムでより多くのことを表現したくなるでしょう。その場合、私は次のリソースのいくつかを調べて、これらの機能を備えた産業品質のツールが、使いやすいインターフェイス(Contracts Rubyなど)を公開するものにのみパッケージ化されることを望んでいることをツールベンダーに知らせます。

3
Eric

最初に、弱く型付けされたものと強く型付けされたもの、静的型と動的型付けの答えに混乱があるように感じます。提供されたOPをリンクすると、明確に区別されます。

強力な型システムは、魅力的なコンパイル時の制限または実行時の機能を持つ型システムです。

弱い型システムは、その制限または機能を欠く型システムです。

たとえば、C、C++、およびJavaは、変数がコンパイル時に型付けされるため、静的に型付けされます。ただし、CおよびC++は、言語がvoid *ポインターおよびキャストを使用して制限をバイパスできるため、弱い型付けと見なされます。 . このトピックの詳細

この区別では、強い型付けがより良い場合があります。失敗が早いほど良い。

しかし、大きなプログラムを書く場合、型システムが重要な役割を果たすとは思いません。 Linuxカーネルは、Cおよびアセンブリで記述された1000万のLOCであり、非常に安定したプログラムと見なされており、私の200 Java行から数マイル離れている可能性があります。 「スクリプト言語」は、大規模なプログラムを書くことに関して悪い評判を被ります、それが不当であるという証拠があることもあります(Python Django、70k以上のLOCなど)

私の意見では、それはすべて品質基準に関するものです。大規模なアプリケーションのスケーラビリティに対する責任は、プログラマーとアーキテクト、およびアプリケーションをクリーンにしてテストし、十分に文書化する意志だけが負うべきです。

2
Arthur Havlicek