「エラーモナド」の版間の差分

提供: tezos-wiki
移動先: 案内検索
(ページの作成:「.. _error_monad: = The Error Monad = This has been adapted from a blog post on ''michelson-lang.com''. If you’re not familiar with monads, go take a few minutes and...」)
 
 
1行目: 1行目:
.. _error_monad:
+
.. _error_monad:
  
= The Error Monad =
+
=エラーMonad =
  
This has been adapted from a blog post on ''michelson-lang.com''.
+
これは、「michelson-lang.com」のブログ記事から修正されています。
  
If you’re not familiar with monads, go take a few minutes and read a tutorial. I personally got a lot out of this <code>paper &lt;http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf&gt;</code>__ by Philip Wadler, but there are a ton of others available online. Find one that works for you. The error monad isn’t terribly scary as Monads go, so once you feel like you understand the gist, come on back and see if you can understand what’s going on.
+
モナドに慣れていない場合は、数分でチュートリアルを読んでください。私はPhilip Wadlerのこの<code> paper <http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf&gt; </code> __から個人的には多くを得ていますが、オンラインで入手できる他の大量のもの。あなたのために働くものを見つけてください。エラーモナドはモナドが行っているようにひどく恐ろしいことではないので、あなたがその要点を理解していると感じたら、戻って何が起こっているのかを理解できるかどうかを見てください。
  
I’m going to omit some convenience operations that a lot of monads provide in the examples below. If you want to add them, they’re not difficult.
+
以下の例では、多くのモナドが提供するいくつかの便利な操作を省略します。追加したい場合は、難しくありません。
  
== Why you want the error monad ==
+
==なぜあなたはエラーを要求していますか?
  
In Tezos, we don’t want to have the node be crashable by an improper input. To avoid this possibility, it was decided that the system should not use exceptions for error handling. Instead, it uses an error monad. This design forces errors to be handled or carried through before an output can be used. Exceptions are still occasionally used, but this is mostly in the client and only for internal errors.
+
Tezosでは、不適切な入力によってノードをクラッシュさせることは望ましくありません。この可能性を回避するために、システムでエラー処理の例外を使用すべきでないと判断されました。代わりに、エラーモナドを使用します。この設計では、出力を使用する前にエラーを処理または伝達するよう強制されます。例外は依然として使用されることがありますが、これは主にクライアント側であり、内部エラーの場合のみです。
  
We also mix in the Lwt library, which we use for concurrency. This is combined with the error monad and is once again used pervasively throughout the codebase. The Lwt monad is a lot like promises in other languages.
+
並行性のために使用するLwtライブラリにも組み込みます。これはエラーモナドと組み合わされ、コードベース全体にわたって再び使用されます。 Lwtモナドは他の言語の約束とよく似ています。
  
Without further ado, let’s write an error monad.
+
さらなる騒ぎをせずに、エラーモナドを書いてみましょう。
  
== A simple version of the error monad ==
+
==単純なエラーモナドのバージョン==
  
Here’s a very simple error monad.
+
ここには非常に単純なエラーモナドがあります。
  
.. code:: ocaml
+
..コード:: ocaml
  
<pre>module Error : sig
+
<pre>モジュールエラー:sig
   type 'a t
+
   タイプ 'a t
   (* Create a value of type t *)
+
   *タイプt *の値を作成します)
   val return : 'a -&gt; 'a t
+
   戻り値: 'a - &gt; 'いや
   (* For when a computation fails *)
+
   *計算が失敗した場合*
   val error : 'a t
+
   値の誤差: 'a t
   (* Apply an operation to a value in the error monad *)
+
   *エラーモナドの値に演算を適用する*
   val (&gt;&gt;?) : 'a t -&gt; ('a -&gt; 'b t) -&gt; 'b t (* bind *)
+
   val(&gt;&gt;?): 'a t - &gt; 'a - &gt; b t) - &gt; 'b t(* bind *
end = struct
+
終了=構造体
   type 'a t = Ok of 'a | Error
+
   タイプ 'a t = Ok of' a |エラー
   let return x = Ok x
+
   return x = Ok x
   let error = Error
+
   let error =エラー
   let (&gt;&gt;?) value func =
+
   let(&gt;&gt ;?)の値func =
     match value with
+
     ?と一致する値
     | Ok x -&gt; func x
+
     | Ok x - &gt; func x
     | Error -&gt; Error
+
     |エラー - &gt;エラー
end</pre>
+
終了</pre>
So, is this what Tezos uses? We actually already have a lot of the structure that we’ll use later. The basic idea is that you return a value that’s correct and return an error if the operation failed. Outside of the error module, you can’t actually introspect an error value. You can only dispatch on the correctness/incorrectness of the value using bind.
+
それで、これはTezosが使うものですか?実際には、後で使用する多くの構造が既に用意されています。基本的な考え方は、正しい値を返し、操作が失敗した場合はエラーを返すことです。エラーモジュールの外側では、実際にエラー値をイントロスペクトすることはできません。バインドを使用して、値の正確性/不正性のみをディスパッチできます。
  
What’s wrong here?
+
ここで何が間違っていますか?
  
* We can’t report any information about an error case
+
*エラーケースに関する情報は報告できません
* We can’t report error traces, something that’s used to improve the quality of error messages throughout Tezos
+
* Tezos全体のエラーメッセージの品質を改善するために使用されるエラートレースは報告できません
* We can’t handle some errors and continue executing
+
*いくつかのエラーは処理できず、実行を継続できません
  
== A slight improvement ==
+
==若干の改善==
  
Let’s now enhance our error reporting by allowing errors to contain a description string. Now we can report messages along with our errors. Is this enough of an improvement? Not really. We don’t have any flexibility about how the printing works. We still can’t create error traces and we can’t handle errors and resume executing the program.
+
エラーに説明文字列を含めることで、エラー報告を強化しましょう。今度はエラーとともにメッセージを報告することができます。これで十分ですか?あんまり。印刷の仕方に柔軟性はありません。エラートレースを作成することはできず、エラーを処理してプログラムの実行を再開することはできません。
  
.. code:: ocaml
+
..コード:: ocaml
 
+
<pre>モジュールエラー:sig
<pre>module Error : sig
+
   タイプ 'a t
   type 'a t
+
   戻り値: 'a - &gt; 'いや
   val return : 'a -&gt; 'a t
+
   valエラー:文字列 - &gt; 'いや
   val error : string -&gt; 'a t
+
   val(&gt;&gt;?): 'a t - &gt; 'a - &gt; b t) - &gt; 'b t(* bind *
   val (&gt;&gt;?) : 'a t -&gt; ('a -&gt; 'b t) -&gt; 'b t (* bind *)
+
   val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
   val print_value : ('a -&gt; string) -&gt; 'a t -&gt; unit
+
終了=構造体
end = struct
+
   タイプ 'a t = Ok of' a |文字列のエラー
   type 'a t = Ok of 'a | Error of string
+
   return x = Ok x
   let return x = Ok x
+
   エラーs =エラーs
   let error s = Error s
+
   let(&gt;&gt ;?)の値func =
   let (&gt;&gt;?) value func =
+
     ?と一致する値
     match value with
+
     | Ok x - &gt; func x
     | Ok x -&gt; func x
+
     |エラーs - &gt;エラーs
     | Error s -&gt; Error s
 
 
   let print_value func = function
 
   let print_value func = function
     | Ok x -&gt; Printf.printf &quot;Success: %s\n&quot; (func x)
+
     | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
     | Error s -&gt; Printf.printf &quot;Error: %s\n&quot; s
+
     |エラーs - &gt; Printf.printf "エラー:%s \ n" s
end</pre>
+
終了</pre>
== Traces ==
+
==トレース==
  
Now that we have the basic structure down, we can add a mechanism to let us include traces. As a note, the error type I had above is exactly the <code>result</code> type from the OCaml standard library. The traces are just lists of error messages. If you have a call you think might fail, and you want to provide a series of errors, you can wrap that result in the <code>trace</code> function. If that call fails, an additional error is added.
+
基本的な構造が完成したので、トレースを含めるためのメカニズムを追加できます。メモとして、上記のエラータイプは、OCaml標準ライブラリの<code> result </code>タイプです。トレースは、エラーメッセージのリストです。失敗したと思われる呼び出しがあり、一連のエラーを返す場合は、その結果を<code> trace </code>関数にラップすることができます。その呼び出しが失敗すると、追加のエラーが追加されます。
  
.. code:: ocaml
+
..コード:: ocaml
  
<pre>module Error : sig
+
<pre>モジュールエラー:sig
   type 'a t
+
   タイプ 'a t
   val return : 'a -&gt; 'a t
+
   戻り値: 'a - &gt; 'いや
   val error : string -&gt; 'a t
+
   valエラー:文字列 - &gt; 'いや
   val (&gt;&gt;?) : 'a t -&gt; ('a -&gt; 'b t) -&gt; 'b t (* bind *)
+
   val(&gt;&gt;?): 'a t - &gt; 'a - &gt; b t) - &gt; 'b t(* bind *
   val print_value : ('a -&gt; string) -&gt; 'a t -&gt; unit
+
   val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
   val trace : string -&gt; 'a t -&gt; 'a t
+
   valトレース:文字列 - &gt; 'a t - &gt; 'いや
end = struct
+
終了=構造体
   type 'a t = ('a, string list) result
+
   タイプ 'a t =' a、string list)result
   let return x = Ok x
+
   return x = Ok x
   let error s = Error [ s ]
+
   エラーとする=エラー[s]
   let (&gt;&gt;?) value func =
+
   let(&gt;&gt ;?)の値func =
     match value with
+
     ?と一致する値
     | Ok x -&gt; func x
+
     | Ok x - &gt; func x
     | Error errs -&gt; Error errs
+
     |エラーエラー - &gt;エラーエラー
 
   let print_value func = function
 
   let print_value func = function
     | Ok x -&gt; Printf.printf &quot;Success: %s\n&quot; (func x)
+
     | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
     | Error [ s ] -&gt; Printf.printf &quot;Error: %s\n&quot; s
+
     |エラー[s] - &gt; Printf.printf "エラー:%s \ n" s
     | Error errors -&gt; Printf.printf &quot;Errors:\t%s\n&quot; (String.concat &quot;\n\t&quot; errors)
+
     |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"エラー)
   let trace error = function
+
   トレースエラー=機能させる
     | Ok x -&gt; Ok x
+
     | Ok x - &gt; OK x
     | Error errors -&gt; Error (error :: errors)
+
     |エラーエラー - &gt;エラー(error :: errors)
end</pre>
+
終了</pre>
== A more descriptive message ==
+
==よりわかりやすいメッセージ==
  
Even though traces are nice, we really want to be able to store more interesting data in the messages. We’re going to use an extensible variant type to do this. Extensible variants allow us to add a new case to a variant type at the cost of exhaustivity checking. We’re going to need two new mechanisms to make this work well. The first is an error registration scheme. In the actual error monad, this involves the data encoding module, which is how all data is encoded/decoded in Tezos. This module is another decently complicated part of the codebase that should probably the subject of a future post. Since you can declare arbitrary new errors, we’ll have a way of adding a printer for each error.
+
トレースはうまくいきますが、メッセージにもっと興味深いデータを格納できるようにしたいと考えています。これを行うには、拡張可能なバリアント型を使用します。拡張可能なバリアントを使用すると、排他性チェックの代償としてバリアントタイプに新しいケースを追加することができます。この作業をうまく行うには、2つの新しいメカニズムが必要になります。 1つはエラー登録方式です。実際のエラーモナドでは、データエンコーディングモジュールが必要です。これは、すべてのデータがTezosでエンコード/デコードされる方法です。このモジュールは、将来の投稿の主題であるはずのコードベースのもう少し複雑な部分です。任意の新しいエラーを宣言できるので、エラーごとにプリンタを追加する方法があります。
  
When we add a new error handler, we’ll use the <code>register_handler</code> function. This function will take a function that takes an error and returns a <code>string option</code>. These functions will look something like this:
+
新しいエラーハンドラを追加するときは、<code> register_handler </code>関数を使用します。この関数はエラーを受け取り、<code>文字列オプション</code>を返す関数をとります。これらの関数は次のようになります。
  
.. code:: ocaml
+
..コード:: ocaml
 
+
<pre> type error + =文字列のExplosion_failure * int ;;
<pre>type error += Explosion_failure of string * int;;
 
  
 
register_error
 
register_error
   (function
+
   (関数
     | Explosion_failure (s, i) -&gt;
+
     | Explosion_failure(s、i) - &gt;
         Some (Printf.sprintf &quot;Everything exploded: %s at %d&quot; s i)
+
         いくつか(Printf.sprintf "すべてが展開されました:%s at%d" s)
     | _ -&gt; None)</pre>
+
     | _&gt;なし)</pre>
I’m also renaming the <code>error</code> function to <code>fail</code>. This is the convention used by the actual Errormonad module. I’m also exposing the <code>'a t</code> type so that you can dispatch on it if you need to. This is used several times in the Tezos codebase.
+
また、<code> error </code>関数の名前を<code> fail </code>に変更します。これは、実際のErrormonadモジュールで使用されている規則です。また、必要に応じてディスパッチできるように<code> 't </code>型を公開しています。これはTezosコードベースで数回使用されます。
  
.. code:: ocaml
+
..コード:: ocaml
  
<pre>module Error : sig
+
<pre>モジュールエラー:sig
   type error = ..
+
   タイプエラー= ..
   type 'a t = ('a, error list) result
+
   タイプ 'a t =' a、エラーリスト)結果
   val return : 'a -&gt; 'a t
+
   戻り値: 'a - &gt; 'いや
   val fail : error -&gt; 'a t
+
   val失敗:error - &gt; 'いや
   val (&gt;&gt;?) : ('a -&gt; 'b t) -&gt; 'a t -&gt; 'b t (* bind *)
+
   val(&gt;&gt;):( 'a - &gt; b t) - &gt; 'a t - &gt; 'b t(* bind *
   val print_value : ('a -&gt; string) -&gt; 'a t -&gt; unit
+
   val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
   val trace : error -&gt; 'a t -&gt; 'a t
+
   valトレース:error - &gt; 'a t - &gt; 'いや
end = struct
+
終了=構造体
   type error = ..
+
   タイプエラー= ..
   type 'a t = ('a, error list) result
+
   タイプ 'a t =' a、エラーリスト)結果
   let fail error = Error [ error ]
+
   失敗エラー=エラー[エラー]
   let return x = Ok x
+
   return x = Ok x
   let (&gt;&gt;?) func = function
+
   let(&gt;&gt ;?)func = function
     | Ok x -&gt; func x
+
     | Ok x - &gt; func x
     | Error errs -&gt; Error errs
+
     |エラーエラー - &gt;エラーエラー
 
   let registered = ref []
 
   let registered = ref []
 
   let register_error handler =
 
   let register_error handler =
     registered := (handler :: !registered)
+
     登録済み= =(ハンドラー::!登録済み)
 
   let default_handler error =
 
   let default_handler error =
     &quot;Unregistered error: &quot; ^ Obj.(extension_name @@ extension_constructor error)
+
     &quot;登録されていないエラー&quot; ^ Obj。(extension_name @@ extension_constructorエラー)
 
   let to_string error =
 
   let to_string error =
     let rec find_handler = function
+
     recをfind_handler = functionにする
       | [] -&gt; default_handler error
+
       | [] - &gt; default_handlerエラー
       | handler :: handlers -&gt;
+
       |ハンドラ::ハンドラ - &gt;
           begin match handler error with
+
           マッチハンドラのエラーを
             | None -&gt; find_handler handlers
+
             |なし - &gt; find_handlerハンドラ
             | Some s -&gt; s
+
             |いくつかのs - &gt; s
           end
+
           終わり
     in find_handler !registered
+
     in find_handler!登録済み
 
   let print_value func = function
 
   let print_value func = function
     | Ok x -&gt; Printf.printf &quot;Success: %s\n&quot; (func x)
+
     | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
     | Error [ s ] -&gt; Printf.printf &quot;Error: %s\n&quot; (to_string s)
+
     |エラー[s] - &gt; Printf.printf "エラー:%s \ n" (to_string s)
     | Error errors -&gt; Printf.printf &quot;Errors:\t%s\n&quot; (String.concat &quot;\n\t&quot; (List.map to_string errors))
+
     |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"(List.map to_string errors))
   let trace error = function
+
   トレースエラー=機能させる
     | Ok x -&gt; Ok x
+
     | Ok x - &gt; OK x
     | Error errors -&gt; Error (error :: errors)
+
     |エラーエラー - &gt;エラー(error :: errors)
end</pre>
+
終了</pre>
== Putting <code>Lwt.t</code> in the mix ==
+
== <code> Lwt.t </code>をミックスに配置する==
 
 
Tezos uses the <code>Lwt library &lt;https://ocsigen.org/lwt/3.2.1/manual/manual&gt;</code>__ for threading. The Lwt monad is mixed in with the error monad module. This requires us to add some extra combinators and reexport some functions from Lwt.
 
  
I’m also renaming the type <code>t</code> to <code>tzresult</code>, as used in the Tezos codebase.
+
Tezosはスレッドのために<code> Lwtライブラリ<https://ocsigen.org/lwt/3.2.1/manual/manual&gt; </code> __を使用します。 Lwtモナドは、エラーモナドモジュールと混在しています。これには、追加のコンビネータを追加し、Lwtからいくつかの関数を再エクスポートする必要があります。
  
.. code:: ocaml
+
また、Tezosコードベースで使用されているように、<code> t </code>のタイプを<code> tzresult </code>に変更します。
  
<pre>module Error : sig
+
..コード:: ocaml
   type error = ..
+
<pre>モジュールエラー:sig
   type 'a tzresult = ('a, error list) result
+
   タイプエラー= ..
   val ok : 'a -&gt; 'a tzresult
+
   タイプ 'a tzresult =' a、エラーリスト)結果
   val return : 'a -&gt; 'a tzresult Lwt.t
+
   大丈夫です: 'a - &gt; 'tzresult
   val error : error -&gt; 'a tzresult
+
   戻り値: 'a - &gt; 'tzresult Lwt.t
   val fail : error -&gt; 'a tzresult Lwt.t
+
   val error:エラー - &gt; 'tzresult
   val (&gt;&gt;?) : 'a tzresult -&gt; ('a -&gt; 'b tzresult) -&gt; 'b tzresult (* bind *)
+
   val失敗:error - &gt; 'tzresult Lwt.t
   val (&gt;&gt;=?) : 'a tzresult Lwt.t -&gt; ('a -&gt; 'b tzresult Lwt.t) -&gt; 'b tzresult Lwt.t
+
   val(&gt;&gt;?): 'a tzresult - &gt; 'a - &gt; b tzresult) - &gt; 'b tzresult(* bind *
   val (&gt;&gt;=) : 'a Lwt.t -&gt; ('a -&gt; 'b Lwt.t) -&gt; 'b Lwt.t
+
   val(&gt;&gt; =): 'a tzresult Lwt.t - 'a - &gt; b tzresult Lwt.t) - &gt; 'b tzresult Lwt.t
   val print_value : ('a -&gt; string) -&gt; 'a tzresult Lwt.t -&gt; unit Lwt.t
+
   val(&gt; =): 'a Lwt.t - 'a - &gt; b Lwt.t) - &gt; 'b Lwt.t
   val trace : error -&gt; 'a tzresult Lwt.t -&gt; 'a tzresult Lwt.t
+
   val print_value:( 'a - &gt; string) - &gt; 'tzresult Lwt.t - &gt;単位Lwt.t
end = struct
+
   valトレース:error - &gt; 'tzresult Lwt.t - &gt; 'tzresult Lwt.t
   type error = ..
+
終了=構造体
   type 'a tzresult = ('a, error list) result
+
   タイプエラー= ..
   let fail error = Lwt.return (Error [ error ])
+
   タイプ 'a tzresult =' a、エラーリスト)結果
   let error error = (Error [ error ])
+
   失敗エラー= Lwt.return(エラー[エラー]
 +
   エラーエラー=(エラー[エラー]
 
   let ok x = Ok x
 
   let ok x = Ok x
   let return x = Lwt.return (ok x)
+
   return x = Lwt.return(ok x)
   let (&gt;&gt;?) value func =
+
   let(&gt;&gt ;?)の値func =
     match value with
+
     ?と一致する値
     | Ok x -&gt; func x
+
     | Ok x - &gt; func x
     | Error errs -&gt; Error errs
+
     |エラーエラー - &gt;エラーエラー
   let (&gt;&gt;=) = Lwt.bind
+
   let(&gt; == Lwt.bind
   let (&gt;&gt;=?) value func =
+
   let(&gt; =?)の値func =
     value &gt;&gt;= function
+
     値>&gt; =関数
     | Ok x -&gt; func x
+
     | Ok x - &gt; func x
     | Error errs -&gt; Lwt.return (Error errs)
+
     |エラーエラー - &gt; Lwt.return(エラーエラー)
 
   let registered = ref []
 
   let registered = ref []
 
   let register_error handler =
 
   let register_error handler =
     registered := (handler :: !registered)
+
     登録済み= =(ハンドラー::!登録済み)
 
   let default_handler error =
 
   let default_handler error =
     &quot;Unregistered error: &quot; ^ Obj.(extension_name @@ extension_constructor error)
+
     &quot;登録されていないエラー&quot; ^ Obj。(extension_name @@ extension_constructorエラー)
 
   let to_string error =
 
   let to_string error =
     let rec find_handler = function
+
     recをfind_handler = functionにする
       | [] -&gt; default_handler error
+
       | [] - &gt; default_handlerエラー
       | handler :: handlers -&gt;
+
       |ハンドラ::ハンドラ - &gt;
           begin match handler error with
+
           マッチハンドラのエラーを
             | None -&gt; find_handler handlers
+
             |なし - &gt; find_handlerハンドラ
             | Some s -&gt; s
+
             |いくつかのs - &gt; s
           end
+
           終わり
     in find_handler !registered
+
     in find_handler!登録済み
 
   let print_value func value =
 
   let print_value func value =
     value &gt;&gt;= fun value -&gt;
+
     値>&gt; =楽しい値 - &gt;
     begin match value with
+
     一致する値を
       | Ok x -&gt; Printf.printf &quot;Success: %s\n&quot; (func x)
+
       | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
       | Error [ s ] -&gt; Printf.printf &quot;Error: %s\n&quot; (to_string s)
+
       |エラー[s] - &gt; Printf.printf "エラー:%s \ n" (to_string s)
       | Error errors -&gt; Printf.printf &quot;Errors:\t%s\n&quot; (String.concat &quot;\n\t&quot; (List.map to_string errors))
+
       |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"(List.map to_string errors))
     end; Lwt.return ()
+
     終わり; Lwt.return()
   let trace error value =
+
   トレースエラー値=
     value &gt;&gt;= function
+
     値>&gt; =関数
     | Ok x -&gt; return x
+
     | Ok x - &gt;戻り値x
     | Error errors -&gt; Lwt.return (Error (error :: errors))
+
     |エラーエラー - &gt; Lwt.return(Error(error :: errors))
end</pre>
+
終了</pre>
== The actual Tezos error monad ==
+
==実際のTezosエラーモナド==
  
The actual Tezos error monad adds a few things. Firstly, there are three categories of errors:
+
実際のTezosのエラーモナドはいくつかを追加します。まず、エラーには3つのカテゴリがあります。
  
* :literal:<code>\</code>Temporary` - An error resulting from an operation that might be valid in the future, for example, a contract’s balance being too low to execute the intended operation. This can be fixed by adding more to the contract’s balance.
+
*:リテラル:<code> \ </code> Temporary` - 契約の残高が低すぎて意図した操作を実行できないなど、将来有効な操作の結果生じるエラーです。これは、契約の残高にさらに追加することで修正できます。
* :literal:<code>\</code>Branch` - An error that occurs in one branch of the chain, but may not occur in a different one. For example, receiving an operation for an old or future protocol version.
+
*:リテラル:<code> \ </code> Branch` - チェーンの1つのブランチで発生しますが、別のブランチでは発生しないエラーです。たとえば、古いプロトコルバージョンまたは将来のプロトコルバージョンの操作を受け取ります。
* :literal:<code>\</code>Permanent` - An error that is not recoverable because the operation is never going to be valid. For example, an invalid ? notation.
+
*:リテラル:<code> \ </code> Permanent` - 操作が決して有効にならないために回復できないエラー。たとえば、無効な?表記
  
The registration scheme also uses data encodings. Here’s an example from the <code>validator &lt;../api/odoc/tezos-node-shell/Tezos_node_shell/Validator/index.html&gt;</code>__:
+
登録スキームでは、データエンコーディングも使用されます。以下は、<code>バリデーター</api / odoc / tezos-node-shell / Tezos_node_shell / Validator / index.html&gt; </code> __の例です。
  
.. code:: ocaml
+
..コード:: ocaml
  
<pre>register_error_kind
+
<pre> register_error_kind
     `Permanent
+
     `永久
     ~id:&quot;validator.wrong_level&quot;
+
     ?id:&quot; validator.wrong_level&quot;
     ~title:&quot;Wrong level&quot;
+
     ?タイトル:&quot;間違ったレベル&quot;
     ~description:&quot;The block level is not the expected one&quot;
+
     ?description:&quot;ブロックレベルは期待されたものではありません&quot;
     ~pp:(fun ppf (e, g) -&gt;
+
     ?pp:(楽しいppf(e、g) - &gt;
 
         Format.fprintf ppf
 
         Format.fprintf ppf
           &quot;The declared level %ld is not %ld&quot; g e)
+
           "宣言されたレベル%1dは%1dではありません" g e)
     Data_encoding.(obj2
+
     Data_encoding。(obj2
                     (req &quot;expected&quot; int32)
+
                     (req "expected" int32)
                     (req &quot;provided&quot; int32))
+
                     (req "提供された" int32))
     (function Wrong_level (e, g)  -&gt; Some (e, g) | _ -&gt; None)
+
     (関数Wrong_level(e、g) - > Some(e、g)| _ - > None)
     (fun (e, g) -&gt; Wrong_level (e, g))</pre>
+
     (fun(e、g) - &gt; Wrong_level(e、g))</pre>
An error takes a category, id, title, description, and encoding. You must specify a function to take an error to an optional value of the encoding type and a function to take a value of the encoded type and create an error value. A pretty printer can optionally be specified, but may also be omitted.
+
エラーには、カテゴリ、ID、タイトル、説明、およびエンコーディングが使用されます。エンコーディングタイプのオプションの値にエラーを返す関数と、エンコードされたタイプの値を取得してエラー値を作成する関数を指定する必要があります。きれいなプリンタを任意に指定することもできますが、省略することもできます。
  
The actual error monad and it’s tracing features can be seen in this function which parses contracts:
+
実際のエラーモナドとそのトレース機能は、この関数では契約を解析します:
  
.. code:: ocaml
+
..コード:: ocaml
  
<pre>let parse_script
+
<pre> let parse_script
   : ?type_logger: (int * (Script.expr list * Script.expr list) -&gt; unit) -&gt;
+
   :?type_logger:(int *(Script.expr list * Script.expr list) - &gt; unit) - &gt;
   context -&gt; Script.storage -&gt; Script.code -&gt; ex_script tzresult Lwt.t
+
   コンテキスト - &gt; Script.storage - &gt; Script.code - &gt; ex_script tzresult Lwt.t
   = fun ?type_logger ctxt
+
   = fun?type_logger ctxt
     { storage; storage_type = init_storage_type }
+
     { ストレージ; storage_type = init_storage_type}
     { code; arg_type; ret_type; storage_type } -&gt;
+
     {コード; arg_type; ret_type; storage_type} - &gt;
     trace
+
     トレース
       (Ill_formed_type (Some &quot;parameter&quot;, arg_type))
+
       (Ill_formed_type(some "parameter"、arg_type))
       (Lwt.return (parse_ty arg_type)) &gt;&gt;=? fun (Ex_ty arg_type) -&gt;
+
       (Lwt.return(parse_ty arg_type))&gt; =? fun(Ex_ty arg_type) - &gt;
     trace
+
     トレース
       (Ill_formed_type (Some &quot;return&quot;, ret_type))
+
       (Ill_formed_type(いくつかの "return"、ret_type))
       (Lwt.return (parse_ty ret_type)) &gt;&gt;=? fun (Ex_ty ret_type) -&gt;
+
       (Lwt.return(parse_ty ret_type))&gt;&gt; =?楽しみ(Ex_ty ret_type) - &gt;
     trace
+
     トレース
       (Ill_formed_type (Some &quot;initial storage&quot;, init_storage_type))
+
       (Ill_formed_type(いくつかの "初期ストレージ"、init_storage_type))
       (Lwt.return (parse_ty init_storage_type)) &gt;&gt;=? fun (Ex_ty init_storage_type) -&gt;
+
       (Lwt.return(parse_ty init_storage_type))&gt; =? fun(Ex_ty init_storage_type) - &gt;
     trace
+
     トレース
       (Ill_formed_type (Some &quot;storage&quot;, storage_type))
+
       (Ill_formed_type(いくつかの "storage"、storage_type))
       (Lwt.return (parse_ty storage_type)) &gt;&gt;=? fun (Ex_ty storage_type) -&gt;
+
       (Lwt.return(parse_ty storage_type))&gt; =?楽しい(Ex_ty storage_type) - &gt;
     let arg_type_full = Pair_t (arg_type, storage_type) in
+
     arg_type_full = Pair_t(arg_type、storage_type)を
     let ret_type_full = Pair_t (ret_type, storage_type) in
+
     ret_type_full = Pair_t(ret_type、storage_type)を
     Lwt.return (ty_eq init_storage_type storage_type) &gt;&gt;=? fun (Eq _) -&gt;
+
     Lwt.return(ty_eq init_storage_type storage_type)&gt;&gt; =?楽しい(Eq _) - &gt;
     trace
+
     トレース
       (Ill_typed_data (None, storage, storage_type))
+
       (Ill_typed_data(None、storage、storage_type))
       (parse_data ?type_logger ctxt storage_type storage) &gt;&gt;=? fun storage -&gt;
+
       (parse_data?type_logger ctxt storage_type storage)&gt; =?楽しいストレージ - &gt;
     trace
+
     トレース
       (Ill_typed_contract (code, arg_type, ret_type, storage_type, []))
+
       (Ill_typed_contract(code、arg_type、ret_type、storage_type、[]))
       (parse_returning (Toplevel { storage_type }) ctxt ?type_logger arg_type_full ret_type_full code)
+
       (parse_returning(Toplevel {storage_type})ctxt?type_logger arg_type_full ret_type_full code)
     &gt;&gt;=? fun code -&gt;
+
     &gt; =?楽しいコード - &gt;
     return (Ex_script { code; arg_type; ret_type; storage; storage_type })</pre>
+
     return(Ex_script {code; arg_type; ret_type; storage; storage_type}</pre>
Each specific type error from the typechecking process is wrapped in a more general error that explains which part of the program was malformed. This improves the error reporting. You can also see the bind operator used between functions to continue only if an error does not occur. This function also operates in the <code>Lwt</code> monad, which is largely hidden via the error monad.
+
型チェックプロセスの各特定の型エラーは、プログラムのどの部分が不正であったかを説明する、より一般的なエラーに包まれています。これにより、エラー報告が改善されます。また、関数間で使用されるバインド演算子が表示され、エラーが発生しない場合にのみ処理を継続することができます。この関数は、<code> Lwt </code>モナドでも動作します。これは、大部分がエラーモナドによって隠されています。

2018年5月31日 (木) 00:32時点における最新版

.. _error_monad:

エラーMonad[編集]

これは、「michelson-lang.com」のブログ記事から修正されています。

モナドに慣れていない場合は、数分でチュートリアルを読んでください。私はPhilip Wadlerのこの paper <http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf> __から個人的には多くを得ていますが、オンラインで入手できる他の大量のもの。あなたのために働くものを見つけてください。エラーモナドはモナドが行っているようにひどく恐ろしいことではないので、あなたがその要点を理解していると感じたら、戻って何が起こっているのかを理解できるかどうかを見てください。

以下の例では、多くのモナドが提供するいくつかの便利な操作を省略します。追加したい場合は、難しくありません。

==なぜあなたはエラーを要求していますか?

Tezosでは、不適切な入力によってノードをクラッシュさせることは望ましくありません。この可能性を回避するために、システムでエラー処理の例外を使用すべきでないと判断されました。代わりに、エラーモナドを使用します。この設計では、出力を使用する前にエラーを処理または伝達するよう強制されます。例外は依然として使用されることがありますが、これは主にクライアント側であり、内部エラーの場合のみです。

並行性のために使用するLwtライブラリにも組み込みます。これはエラーモナドと組み合わされ、コードベース全体にわたって再び使用されます。 Lwtモナドは他の言語の約束とよく似ています。

さらなる騒ぎをせずに、エラーモナドを書いてみましょう。

単純なエラーモナドのバージョン[編集]

ここには非常に単純なエラーモナドがあります。

..コード:: ocaml

モジュールエラー:sig
  タイプ 'a t
  (*タイプt *の値を作成します)
  戻り値: 'a - &gt; 'いや
  (*計算が失敗した場合*)
  値の誤差: 'a t
  (*エラーモナドの値に演算を適用する*)
  val(&gt;&gt;?): 'a t - &gt; ( 'a - &gt; b t) - &gt; 'b t(* bind *)
終了=構造体
  タイプ 'a t = Ok of' a |エラー
  return x = Ok x
  let error =エラー
  let(&gt;&gt ;?)の値func =
    ?と一致する値
    | Ok x - &gt; func x
    |エラー - &gt;エラー
終了

それで、これはTezosが使うものですか?実際には、後で使用する多くの構造が既に用意されています。基本的な考え方は、正しい値を返し、操作が失敗した場合はエラーを返すことです。エラーモジュールの外側では、実際にエラー値をイントロスペクトすることはできません。バインドを使用して、値の正確性/不正性のみをディスパッチできます。

ここで何が間違っていますか?

  • エラーケースに関する情報は報告できません
  • Tezos全体のエラーメッセージの品質を改善するために使用されるエラートレースは報告できません
  • いくつかのエラーは処理できず、実行を継続できません

若干の改善[編集]

エラーに説明文字列を含めることで、エラー報告を強化しましょう。今度はエラーとともにメッセージを報告することができます。これで十分ですか?あんまり。印刷の仕方に柔軟性はありません。エラートレースを作成することはできず、エラーを処理してプログラムの実行を再開することはできません。

..コード:: ocaml

モジュールエラー:sig
  タイプ 'a t
  戻り値: 'a - &gt; 'いや
  valエラー:文字列 - &gt; 'いや
  val(&gt;&gt;?): 'a t - &gt; ( 'a - &gt; b t) - &gt; 'b t(* bind *)
  val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
終了=構造体
  タイプ 'a t = Ok of' a |文字列のエラー
  return x = Ok x
  エラーs =エラーs
  let(&gt;&gt ;?)の値func =
    ?と一致する値
    | Ok x - &gt; func x
    |エラーs - &gt;エラーs
  let print_value func = function
    | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
    |エラーs - &gt; Printf.printf "エラー:%s \ n" s
終了

トレース[編集]

基本的な構造が完成したので、トレースを含めるためのメカニズムを追加できます。メモとして、上記のエラータイプは、OCaml標準ライブラリの result タイプです。トレースは、エラーメッセージのリストです。失敗したと思われる呼び出しがあり、一連のエラーを返す場合は、その結果を trace 関数にラップすることができます。その呼び出しが失敗すると、追加のエラーが追加されます。

..コード:: ocaml

モジュールエラー:sig
  タイプ 'a t
  戻り値: 'a - &gt; 'いや
  valエラー:文字列 - &gt; 'いや
  val(&gt;&gt;?): 'a t - &gt; ( 'a - &gt; b t) - &gt; 'b t(* bind *)
  val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
  valトレース:文字列 - &gt; 'a t - &gt; 'いや
終了=構造体
  タイプ 'a t =(' a、string list)result
  return x = Ok x
  エラーとする=エラー[s]
  let(&gt;&gt ;?)の値func =
    ?と一致する値
    | Ok x - &gt; func x
    |エラーエラー - &gt;エラーエラー
  let print_value func = function
    | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
    |エラー[s] - &gt; Printf.printf "エラー:%s \ n" s
    |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"エラー)
  トレースエラー=機能させる
    | Ok x - &gt; OK x
    |エラーエラー - &gt;エラー(error :: errors)
終了

よりわかりやすいメッセージ[編集]

トレースはうまくいきますが、メッセージにもっと興味深いデータを格納できるようにしたいと考えています。これを行うには、拡張可能なバリアント型を使用します。拡張可能なバリアントを使用すると、排他性チェックの代償としてバリアントタイプに新しいケースを追加することができます。この作業をうまく行うには、2つの新しいメカニズムが必要になります。 1つはエラー登録方式です。実際のエラーモナドでは、データエンコーディングモジュールが必要です。これは、すべてのデータがTezosでエンコード/デコードされる方法です。このモジュールは、将来の投稿の主題であるはずのコードベースのもう少し複雑な部分です。任意の新しいエラーを宣言できるので、エラーごとにプリンタを追加する方法があります。

新しいエラーハンドラを追加するときは、 register_handler 関数を使用します。この関数はエラーを受け取り、文字列オプションを返す関数をとります。これらの関数は次のようになります。

..コード:: ocaml

 type error + =文字列のExplosion_failure * int ;;

register_error
  (関数
    | Explosion_failure(s、i) - &gt;
        いくつか(Printf.sprintf "すべてが展開されました:%s at%d" s)
    | _&gt;なし)

また、 error 関数の名前を fail に変更します。これは、実際のErrormonadモジュールで使用されている規則です。また、必要に応じてディスパッチできるように 't 型を公開しています。これはTezosコードベースで数回使用されます。

..コード:: ocaml

モジュールエラー:sig
  タイプエラー= ..
  タイプ 'a t =(' a、エラーリスト)結果
  戻り値: 'a - &gt; 'いや
  val失敗:error - &gt; 'いや
  val(&gt;&gt;):( 'a - &gt; b t) - &gt; 'a t - &gt; 'b t(* bind *)
  val print_value:( 'a - &gt; string) - &gt; 'a t - &gt;単位
  valトレース:error - &gt; 'a t - &gt; 'いや
終了=構造体
  タイプエラー= ..
  タイプ 'a t =(' a、エラーリスト)結果
  失敗エラー=エラー[エラー]
  return x = Ok x
  let(&gt;&gt ;?)func = function
    | Ok x - &gt; func x
    |エラーエラー - &gt;エラーエラー
  let registered = ref []
  let register_error handler =
    登録済み= =(ハンドラー::!登録済み)
  let default_handler error =
    &quot;登録されていないエラー&quot; ^ Obj。(extension_name @@ extension_constructorエラー)
  let to_string error =
    recをfind_handler = functionにする
      | [] - &gt; default_handlerエラー
      |ハンドラ::ハンドラ - &gt;
          マッチハンドラのエラーを
            |なし - &gt; find_handlerハンドラ
            |いくつかのs - &gt; s
          終わり
    in find_handler!登録済み
  let print_value func = function
    | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
    |エラー[s] - &gt; Printf.printf "エラー:%s \ n" (to_string s)
    |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"(List.map to_string errors))
  トレースエラー=機能させる
    | Ok x - &gt; OK x
    |エラーエラー - &gt;エラー(error :: errors)
終了

Lwt.t をミックスに配置する[編集]

Tezosはスレッドのために Lwtライブラリ<https://ocsigen.org/lwt/3.2.1/manual/manual> __を使用します。 Lwtモナドは、エラーモナドモジュールと混在しています。これには、追加のコンビネータを追加し、Lwtからいくつかの関数を再エクスポートする必要があります。

また、Tezosコードベースで使用されているように、 t のタイプを tzresult に変更します。

..コード:: ocaml

モジュールエラー:sig
  タイプエラー= ..
  タイプ 'a tzresult =(' a、エラーリスト)結果
  大丈夫です: 'a - &gt; 'tzresult
  戻り値: 'a - &gt; 'tzresult Lwt.t
  val error:エラー - &gt; 'tzresult
  val失敗:error - &gt; 'tzresult Lwt.t
  val(&gt;&gt;?): 'a tzresult - &gt; ( 'a - &gt; b tzresult) - &gt; 'b tzresult(* bind *)
  val(&gt;&gt; =): 'a tzresult Lwt.t - ( 'a - &gt; b tzresult Lwt.t) - &gt; 'b tzresult Lwt.t
  val(&gt; =): 'a Lwt.t - ( 'a - &gt; b Lwt.t) - &gt; 'b Lwt.t
  val print_value:( 'a - &gt; string) - &gt; 'tzresult Lwt.t - &gt;単位Lwt.t
  valトレース:error - &gt; 'tzresult Lwt.t - &gt; 'tzresult Lwt.t
終了=構造体
  タイプエラー= ..
  タイプ 'a tzresult =(' a、エラーリスト)結果
  失敗エラー= Lwt.return(エラー[エラー])
  エラーエラー=(エラー[エラー])
  let ok x = Ok x
  return x = Lwt.return(ok x)
  let(&gt;&gt ;?)の値func =
    ?と一致する値
    | Ok x - &gt; func x
    |エラーエラー - &gt;エラーエラー
  let(&gt; =)= Lwt.bind
  let(&gt; =?)の値func =
    値>&gt; =関数
    | Ok x - &gt; func x
    |エラーエラー - &gt; Lwt.return(エラーエラー)
  let registered = ref []
  let register_error handler =
    登録済み= =(ハンドラー::!登録済み)
  let default_handler error =
    &quot;登録されていないエラー&quot; ^ Obj。(extension_name @@ extension_constructorエラー)
  let to_string error =
    recをfind_handler = functionにする
      | [] - &gt; default_handlerエラー
      |ハンドラ::ハンドラ - &gt;
          マッチハンドラのエラーを
            |なし - &gt; find_handlerハンドラ
            |いくつかのs - &gt; s
          終わり
    in find_handler!登録済み
  let print_value func value =
    値>&gt; =楽しい値 - &gt;
    一致する値を
      | Ok x - &gt; Printf.printf&quot;成功:%s \ n&quot; (func x)
      |エラー[s] - &gt; Printf.printf "エラー:%s \ n" (to_string s)
      |エラーエラー - &gt; Printf.printf&quot;エラー:\ t%s \ n&quot; (String.concat "\ n \ t"(List.map to_string errors))
    終わり; Lwt.return()
  トレースエラー値=
    値>&gt; =関数
    | Ok x - &gt;戻り値x
    |エラーエラー - &gt; Lwt.return(Error(error :: errors))
終了

実際のTezosエラーモナド[編集]

実際のTezosのエラーモナドはいくつかを追加します。まず、エラーには3つのカテゴリがあります。

  • :リテラル: \ Temporary` - 契約の残高が低すぎて意図した操作を実行できないなど、将来有効な操作の結果生じるエラーです。これは、契約の残高にさらに追加することで修正できます。
  • :リテラル: \ Branch` - チェーンの1つのブランチで発生しますが、別のブランチでは発生しないエラーです。たとえば、古いプロトコルバージョンまたは将来のプロトコルバージョンの操作を受け取ります。
  • :リテラル: \ Permanent` - 操作が決して有効にならないために回復できないエラー。たとえば、無効な?表記

登録スキームでは、データエンコーディングも使用されます。以下は、バリデーター</api / odoc / tezos-node-shell / Tezos_node_shell / Validator / index.html&gt; __の例です。

..コード:: ocaml

 register_error_kind
    `永久
    ?id:&quot; validator.wrong_level&quot;
    ?タイトル:&quot;間違ったレベル&quot;
    ?description:&quot;ブロックレベルは期待されたものではありません&quot;
    ?pp:(楽しいppf(e、g) - &gt;
        Format.fprintf ppf
          "宣言されたレベル%1dは%1dではありません" g e)
    Data_encoding。(obj2
                     (req "expected" int32)
                     (req "提供された" int32))
    (関数Wrong_level(e、g) - > Some(e、g)| _ - > None)
    (fun(e、g) - &gt; Wrong_level(e、g))

エラーには、カテゴリ、ID、タイトル、説明、およびエンコーディングが使用されます。エンコーディングタイプのオプションの値にエラーを返す関数と、エンコードされたタイプの値を取得してエラー値を作成する関数を指定する必要があります。きれいなプリンタを任意に指定することもできますが、省略することもできます。

実際のエラーモナドとそのトレース機能は、この関数では契約を解析します:

..コード:: ocaml

 let parse_script
  :?type_logger:(int *(Script.expr list * Script.expr list) - &gt; unit) - &gt;
  コンテキスト - &gt; Script.storage - &gt; Script.code - &gt; ex_script tzresult Lwt.t
  = fun?type_logger ctxt
    { ストレージ; storage_type = init_storage_type}
    {コード; arg_type; ret_type; storage_type} - &gt;
    トレース
      (Ill_formed_type(some "parameter"、arg_type))
      (Lwt.return(parse_ty arg_type))&gt; =? fun(Ex_ty arg_type) - &gt;
    トレース
      (Ill_formed_type(いくつかの "return"、ret_type))
      (Lwt.return(parse_ty ret_type))&gt;&gt; =?楽しみ(Ex_ty ret_type) - &gt;
    トレース
      (Ill_formed_type(いくつかの "初期ストレージ"、init_storage_type))
      (Lwt.return(parse_ty init_storage_type))&gt; =? fun(Ex_ty init_storage_type) - &gt;
    トレース
      (Ill_formed_type(いくつかの "storage"、storage_type))
      (Lwt.return(parse_ty storage_type))&gt; =?楽しい(Ex_ty storage_type) - &gt;
    arg_type_full = Pair_t(arg_type、storage_type)を
    ret_type_full = Pair_t(ret_type、storage_type)を
    Lwt.return(ty_eq init_storage_type storage_type)&gt;&gt; =?楽しい(Eq _) - &gt;
    トレース
      (Ill_typed_data(None、storage、storage_type))
      (parse_data?type_logger ctxt storage_type storage)&gt; =?楽しいストレージ - &gt;
    トレース
      (Ill_typed_contract(code、arg_type、ret_type、storage_type、[]))
      (parse_returning(Toplevel {storage_type})ctxt?type_logger arg_type_full ret_type_full code)
    &gt; =?楽しいコード - &gt;
    return(Ex_script {code; arg_type; ret_type; storage; storage_type})

型チェックプロセスの各特定の型エラーは、プログラムのどの部分が不正であったかを説明する、より一般的なエラーに包まれています。これにより、エラー報告が改善されます。また、関数間で使用されるバインド演算子が表示され、エラーが発生しない場合にのみ処理を継続することができます。この関数は、 Lwt モナドでも動作します。これは、大部分がエラーモナドによって隠されています。