きみはねこみたいなにゃんにゃんなまほう

ねこもスクリプトをかくなり

Saltstack の highstate や low とは何なのか

最近 Saltstack を触っております。

デバッグがてらに state.show_ 系のコマンドを打つことが多いのですが似たようなコマンドが多くて初見ではちんぷんかんぷんなんですよね。

  • state.show_sls
  • state.show_low_sls
  • state.show_highstate
  • state.show_lowstate

この highlow といった用語はあからさまに Saltstack ドメインの言葉遣いなのですが、ではそれが意味するところとは何なのか、今回はそれを押さえてより理解度深く Slatstack を使えるようになろうと思います。

といっても公式のドキュメントの簡単な和訳や要約なので特に大したことはしてません。

State System Layers

State System Layers に挙げられている項目は上から順に Saltstack における State 系の概念のローレイヤーからのスタックになっているのでそれを順に見ていきたいと思います。

Function Call

  • State System における最もローレイヤーな要素
  • Slatstack における State の実行とは個々の State Function の実行と言える
  • pkg.installed などの関数の実行がこれに当たる

Low Chunk

  • YAML(JSON?) で書かれたデータ構造
  • Salt State Compiler のコンパイル結果がこれ(Low Chunk)の集積物になる
  • Function Call が「実行すること」だとすると Low Chunk は 「実行するとその Function Call が実行されるもの」 と言えそう(ややこしい)

Low State

  • Low Chunk が処理順に並べられたリスト
  • 推測混じりで言えば、ここまでは「どの環境のどのホストで」というものを 考慮しない 純粋なデータ構造・実装や処理のことを指していると思われる*1

High Data

  • SLS Files により構成される YAML として表されるデータ構造
  • SLS Files をまとめたもの、という説明にとどまっている
  • この時点では特に「何のためのもの」というところには触れられていない

SLS

  • SLS レイヤーは High Data の論理レイヤーであると(ちょっと意味が読み取れない)
    • 最終的にデータ構造さえ提供されればその生成過程は問わないとある
  • 用語として SLS Files と SLS Formula は区別したいらしい
    • かつては SLS は単体のファイルで構成されており SLS Formula は SLS Files とも呼ばれていた
    • 現在の SLS は Pillar の内容などが適用され動的に生成されるので SLS Formula と呼ぶのが良い
    • この説明の流れだと SLS Files は個々の *.sls ファイルを指すと考えればいいのだろうか

Highstate

  • SLS を読み込んでどの minion に何を実行させるべきかを判断する

Orchestrate

  • これは Salt の更に上位でやってくれとある

まとめ

ここまで読んで来た内容を今度は逆順におさらいしてみると...

  • Highstate とは各 minion ごとの Low State が集まったもの
  • Low State とは Low Chunk のリストで、Low Chunk とは個々の Function Call を行うためのデータ

と読み取れます。つまり当初気になっていた Slatstack 的な high/low の意味合いとしては

  • 細かい個々の State 管理の項目のことが low
  • 環境やグループなど minion ごとの設定を含めた全体の状態管理が high

...と呼ばれているのかな、という雰囲気はつかめたような気がします。

コマンドの解説を再度読んでみる

以上の前提知識をもとに改めて コマンドリファレンス を読んでみます。

  • state.show_highstate
    • salt master から highstate を取得し表示する
  • state.show_low_sls
    • SLS から Low Data 形式に落とし込まれたものを表示する
  • state.show_lowstate
    • 指定された minion に適用される Low Data を表示する
  • state.show_sls
    • master 上の指定された sls file の State Data を表示する
    • State Data ってなんだろう...

よく見たら high/low の違いと state/sls の違いの組み合わせなんですよね。手元に試せる環境がないので何も言えないのですが、まだ違いがよくわからないんですよね... また気が向いたら今度は触りながら確認していきたいです。

*1:わかりづらい例えをすれば、我々のプロジェクトではこの筆記具のことを「鉛筆」と呼びそれはこういう構造・性質を持っています、ということを言っているのみで、それが運用上どう使われているかはこの段階では触れられていない。

TypeScript の絶対パスの import をプロジェクトルートから辿るようにする

何やら TypeScript では絶対パスでプロジェクトルートから import を行えるようだと耳にしました。これで相対パス地獄から逃れることができますね。早速動作確認していきたいと思います。

使用した TypeScript のバージョンは 3.0.3 です。

動作確認

以下のようなディレクトリ構成で動作確認を進めていきます。

.
├── package.json
├── src
│   ├── app-global-lib.ts
│   ├── index.ts
│   └── nested
│       └── nested
│           └── super-nested-lib.ts
└── tsconfig.json

src/app-global-lib.ts

src 直下の src/app-global-lib.ts からは適当な定数を一つ export します。

export const APP_GLOBAL_CONST = 1

src/nested/nested/super-nested-lib.ts

それを何階層かネストしたファイル src/nested/nested/super-nested-lib.ts から import します。src/... のように絶対パスで指定しています。

import { APP_GLOBAL_CONST } from 'src/app-global-lib'

export const SUPER_NESTED_CONST = 2

このままだと Cannot find module のエラーが出るのですが、ここで tsconfig.json

"baseUrl": "./"

コメントアウトを外すと絶対パスがプロジェクトルートからのパスとして解釈され、コンパイルが通るようになります。

src/index.ts

src/index.js では今まで通り相対パスimport します。node_modules のライブラリも問題なく参照できるか確認したかったので適当に uuidimport しています。

import { APP_GLOBAL_CONST } from './app-global-lib'
import { SUPER_NESTED_CONST } from './nested/nested/super-nested-lib'
import uuid from 'uuid'

console.log(APP_GLOBAL_CONST + SUPER_NESTED_CONST, uuid())

これで無事 1 + 2 で 3 と UUID が表示されました。

感想

とても見やすくなりました。通常の Node.js でもできると嬉しいです。

ところで 以前GitHub - piotrwitek/react-redux-typescript-guide: The complete guide to static typing in "React & Redux" apps using TypeScript を読んだ時に気になったのですが

"baseUrl": "./",
"paths": { "@src/*": ["src/*"] },

という設定を tsconfig.json に行うと import @src/app-global-lib という参照の仕方もできるようなのですが、どちらがオススメなのでしょうか。

参考

react-redux-typescript-guide を読んだ感触

Redux + TypeScript の検証作業で疲弊していたところで react-redux-typescript-guide を読んだら思ったより色々まとまっていて、検証のモチベーションがちょっと高まりました、という感想文です。

現在 Redux + TypeScript でアプリを作ろうと色々調べています。 業務でも趣味でも Redux を使った経験はほとんどありません。 認証情報を格納するためのストアとしてシンプルな reducer と数個の action を定義した程度です。

前回のエントリでは typescript-fsatypesafe-actions についてごく簡単に触った感触について書きました。

dispatch に連動して副作用を実現する redux-observableredux-thunk については何度か手元の試作プロジェクトで軽く触った程度です。 軽く触った程度ですが Redux + Typescript の荒波には十分揉まれました。 分かりやすい進捗が出せなくてそろそろ辛いです。

そして react-redux-typescript-guide というガイドを見つけました。

自分でライブラリを探している途中で何度かページ自体は目にしていたのですが、改めて読んでみたところかなり内容がまとまっているようでした。ここまで荒波にまみれてなんとなく掴めてきた理解を整理する上でも、一旦頭を空にして読む価値のありそうなガイドです。写経や読経は世俗の煩悩にまみれた状態でこそ効力を示します。

このガイドでメインで使われているのは typesafe-actionsredux-observable の組み合わせです。

今回は react-redux-typescript-guide の概要をまとめ...るようなことはせず、どの部分を読んでどういう理解になったか、何が得られたかの、ただの感想文を残していこうと思います。

コード類はほとんど引用していないので読みづらいですが済みません。

Type Definitions & Complementary Libraries

まずは補助的に使うライブラリの紹介です。 typesafe-actions はむしろメインで使うライブラリですね。

React Type Cheatsheet

React + TypeScript のベースとなる型情報です。この辺はある程度 React + TypeScript の経験があれば大体把握しているはずですが、それでも新しく気づいたことがあったので、網羅性というものの価値を感じます。

  • React.CSSPRoperties
    • Material-UI でたまに JSS 用に型定義が欲しくなることがありますが React に含まれていたのは初めて知りました

Component Type Pattern

チートシートの続きで、頻出パターンの逆引き一覧になっています。ここも発見が多いです。実装の段階でお世話になりそうです。

Stateful Components

  • Stateful Components って constructor 無しでもいける
  • defaultProps の例はちょっとわかりづらい(というより読みづらい)

Render Props

Apollo なんかで <Query>{({ loading }) => ... }</Query> とかやるやつですね。 そういえば Render Props と HOC の良し悪しってあまり把握してないんですよね...

Higher-Order Components

そして Higher-Order Components の書き方です。色々理解していないことだらけでした。

  • typeclass の中で定義できる
  • Enhanced なコンポーネントに名前をちゃんとつけててえらい
  • そういえば recompose はこのガイドの中では触れられていないのが気になりました
  • Error Boundary の例もあるのは嬉しいです

以前、FlowApollo を利用していたことがあるので、そこまで苦労しないで読めました。

bindActionCreators

Redux とのつなぎの部分で利用する bindActionCreators (初めて知りました)でワンポイントです。

Redux の bindActionCreators を使うときは props 内で () => void を使うとコンパイルエラーになるけれど () => any で代用可能、という注意書きがあり、このガイドの例もそれで実装されているみたいです。

Redux

そして来ました Redux です。 typesafe-actions を使って action を定義していきますが、 詳しくは The Mighty Tutorial を読んでね、と書いてありますね。

そして 前回 気になったところですが、リンク先にも書いてあります。 大事そうなのでここだけ原文で引用します。

WARNING: When using string constants for action type, please be sure to use simple string literals. Don't use string concatenation, template strings or object map because your type will lose the type information, widening to it's supertype string (this is how TypeScript works).

String Literal を型推論に利用しているので template strings や object map を使うと型が string に落ちてしまい action type の型情報が得られなくなる(= ペイロードの型付けが行えなくなる)ということです。 大変そうですが地道に全ての action の名前をつけていくしかなさそうです。

型付けに別の手法を取っている typescript-fsa にはこういう制約はなさそうです。ここで規模のあるアプリを作成した時に、最終的な使い勝手がどう変わるかが、今後の検証でも気になるところです。

Reducers

State の定義には readonly を使うといいよという話。 ReadonlyArrayutility-typesDeepReadonly も便利そうです。

他は TypeScript のスマートな型解析で if ステートメント内の型がその条件により type narrowing されるという話ですね。

Store Configuration

特に目立ったところはなく、以下の微妙な違いに気をつけてさえすればいいと思います。

  • RootAction は全ての Action の Union 型
  • RootStatecombineReducer された(構造化された) State を示す型

いちいち Root とつけるかは好みですが、実際、プロジェクトを構造化した状態で import を大量に行なっていたりすると、実際は何を参照しているのかあやふやになってくるので、特別な名前で参照できると安心感があります。

他に書かれているのは redux-observable の epic を middleware に仕込むところくらいです。

Async Flow

これは The Mighty Tutorial へのリンクで済まされています。これについてはまたそのうち別途書くかもしれません。

Selectors

これは完全に初見の概念だったのですが、Selector とは Store 上のあるデータに別のデータを適用した結果をキャッシュしておき、その結果が依存する State の変更があるまでそれを保持し続けることを行うライブラリの総称のようです。Computing Derived Data - Redux にも項目があります。

Vue.js の computed に近いといえば近いのでしょうか(あれはテンプレート中で参照するためのものなので用途が違うかもしれませんが)。

Tools & Recipes

個人的に一番嬉しかったところかもしれません。よく利用されるツールの設定例が紹介されています。

  • Common Npm Scripts
  • tsconfig.json
    • @src/... でプロジェクトルート下の ./src を参照するやり方があるのを初めて知りました
      • でもどうやらデフォルトで import src/... が動作するみたいですね...
      • この辺はまた検証してみたいです
    • lib で 何を指定したらいいかの答えはここにありました(毎回迷うんですよね...)
  • Vendor Types Augmentation
    • ライブラリの型定義がおかしい場合に quick fix してやり過ごす方法も載っています

感想

...と、一通り読んできましたが、ただ単に読んだだけで特に手を動かして検証を行なったわけではないので、何ができるようになったではありません。それでも多くの試したいことが発見できたので、技術検証のモチベーションを高める効果はあったと思います。もしくは単に鮮度のいいネタを提供してもらえたと言うべきでしょうか。

とりあえず Redux + TypeScript にかき乱された頭の中は整流されたような気がします。 これでまた型システムの闇に立ち向かえそうな気がします。

気になったところ

ガイドを読んでいて、後で調べたいと思ったところです。

  • HOC と Render Props の比較
    • 両者とも同様にDI的なことを行なっているので、手法の差がどう評価されているのか気になります
  • TypeScript の import path の設定
    • よく ../../../api のように ./src 直下近くに置いたライブラリが参照しづらくなる問題が TypeScript では tsconfig.jsonbaseUrl で解決されているみたいですが、その動作について精査してみたいです
    • import @src/... のパターンと import src/... のパターンがあるみたいですが、どちらが推奨されているのかも気になります

Redux + TypeScript 周りの調査

最近 Redux に手を伸ばし始めました。JavaScript で経験を積まずにいきなり TypeScript から始めたせいか無駄に試行錯誤したりハマっている気がしますが、めげずに界隈の状況把握から進めていきたいと思います。

今回は主に typesafe-actionstypescript-fsa を 1日 触った感じを軽く書いていきます。特に網羅的には書いていません。

side-effect 系ライブラリ

今回は深くは触れませんが、前提として Redux の action の dispatch に際しての副作用を提供するライブラリの代表的なところを挙げていきます。

この中では redux-thunkredux-observable を少し使ってみました。redux-observable を利用するには RxJS をある程度触って理解することが必須となります。そして一応書けたコードを見て「これって保守性どうなのだろう」と思わされたので、もう少しこの辺は調査を進めたいところです。

しかし React の勉強をして Redux はそれとはまた別の勉強が必要になり、さらに RxJS の勉強もしないといけないとなると、新しくアサインされた人には辛いプロジェクトになるかもしれないですね(というより保守人員を求めるのであれば Redux ってどうなんでしょうね)。

action 系ライブラリ

action の定義を楽にしたり定型化したりするライブラリですね。FSA(Flax Standard Action) に基づいた設計の action を生成するものをよく見かけます。

という 2 つのライブラリが目につきます。勢いにも知名度にもそれほど違いがないようなので両者で迷いますね。簡単に触った感じの違いをそれぞれ見ていきます。

typescript-fsa の action 定義

const login = actionCreator.async<
  LoginParams,
  LoginInfo,
  ErrorResponse
>('LOGIN')

のように定義します。これで login.started/done/failed という非同期処理用の 3 種類の action が定義されます。ペイロードの type narrowing には isType が用いられ User-Defined Type Guards で実現されているみたいです。

typesafe-actions の action 定義

const login = createAsyncAction(
  'LOGIN_REQUEST',
  'LOGIN_SUCCESS',
  'LOGIN_FAILURE'
)<LoginParams, LoginInfo, ErrorResponse>();

のように定義します。これで login.request/success/failure という非同期処理用の 3 種類の action が定義されます。typescript-fsa との違いとして 3 種類のアクション名を定義しなければならないのですが action の数が増えてくると結構面倒です。あるデータモデルのCRUD操作をしようとすると複数GETも含めて 15 個の名前を定義せねばならず、さらにそれがデータモデル分必要になってくるという。

関数なんかで生成しようとしても、現状の TypeScript には String Literal をマッピングするような操作(プレフィックスをつけた新たな String Literal を定義する等)ができず、関数を通すとただの string 型に落ちてしまうという弊害があります。typesafe-actions の type narrowing は String Literal により実現されているようなので、その恩恵にあずかるには全てのアクション名をきちんと定義する必要がありそうです。

action 定義ライブラリについての所感

パッと見の定義の楽さで言えば typescript-fsa を取りたいところですが、そこからさらに redux-thunk を使うか redux-observable を使うかの選択次第で、実は typesafe-actions が使いやすいかもしれないですし、そうでないかもしれません。

さらにこれがより実際的になってくると fetch 対象のデータモデル数が 7-8 種類程度の分量になってきたり、combineReducers などの階層化が必要になってきたりして、その場合もちゃんと分かりやすく構造化して実装を整理できるのか、そして型定義はどうなるか、そういったこと次第でもまた選択肢が変わってくるかもしれません。

実際に typescript-fsatypesafe-actions を行ったり来たりした感想ですが、そこまで大規模な書き換えは必要にならなかったので、現在の実装が詰み気味になり対向が気になってきたら書き換える、という方針も取れなくはないかもしれません(不安)。主に書き換えたポイントとしては以下の通りです。

  • import するライブラリ
  • 非同期用の action の 3 種類のフィールド名
  • type narrowing の方法

ただ typescript-fsa-reducers というライブラリを使って reducer を書いていると行き来がしづらくなるので方針が固まるまでは if で地道に reducer を書くようにとどめておくのがいいかもしれません。

...という、Redux + TypeScript + Side Effect という交差点では、やはりというか組み合わせが爆発しているのだなという、ため息に似た現状振り返りなのでした。この辺はもう、検証しながら正解スタックを探す覚悟を決めねばやっていけません。

といってもそもそも Redux をそこまでまだ理解していないので、まずはそこから、プラクティスをかき集めていきたいです。

...Apollo が懐かしいです。そう、初めは Apollo が提供している機能を「別に Redux でハンドメイドできるんじゃないか」と思って書き始めたのでした。分厚いレイヤーに嫌気がさして Redux に手を伸ばしたわけですが、自分で書くと想像以上に込み入ったコードになってしまい...フレームワークって偉大ですね、という身も蓋もない着地点が視界の端に移り始めてきたのでした。

React のファイル名から受ける想定利用法

React を触り始めて 1 年くらい経過しましたが、始めの頃はモジュールのファイル名の命名規則がよくわからなくて迷走していたのをふと思い出しました。

例えば「load data」の 2 語からなるファイル名を考えると、以下 の 3 パターンが考えられます。

  • load-data.js (kebab-case)
  • loadData.js (lowerCamelCase)
  • LoadData.js (UpperCamelCase)

ある程度慣れてくると、ファイル名から作者が想定しているそのモジュールの利用法が読み取れるようになります。 今回はその個人的な感覚のまとめになります。

load-data.js から感じ取る利用法

旧来(いつだろう)の JavaScript 的には一番自然に見えるモジュールファイル名ですね。 「各メンバを個々に読み込んで利用するタイプのモジュール」と受け取れます。

import { func1, func2, const1 } from './load-data'

const result1 = func1() + const1
const result2 = func2()

loadData.js から感じ取る利用法

初見で「おや、命名規則が読み取れない...」と思わされたモジュール名です。

これは「default export されている function のメンバが存在するモジュール」と受け取れます。

import loadData from './loadData'

const myData = loadData()

default export しているメンバがあるので、可能ならファイル名と同じ名前で利用してね、というメッセージを感じますね。

LoadData.js から感じ取る利用法

これも上のパターンに似ていて「default export されている class のメンバが存在するモジュール」と受け取れます。

import LoadData from './LoadData'

const loadData = new LoadData()

もしくは React などの JSX であればコンポーネントとして利用できる、という意味も追加されます。 この場合の実装はクラス・関数問わずになりますね。

import LoadData from './LoadData'

function MyComponent() {
  return <LoadData />
}

JSX の場合は new するクラスか、それともコンポーネントか、どちらを指すモジュールなのかは仕様や実装を見なければ判断できませんね。

Material UI + TypeScript で withStyles (JSS) を型付きで使う

Material-UI を使い始めて数日が経ち、ようやくJSSの思想が飲み込めてきました。 当初の違和感もだんだん薄れ、今では(今のところは)確かに楽だよねという感覚になってきております。

今回は型定義についてです。

import * as React from 'react'
import { createStyles, Theme, WithStyles, withStyles } from '@material-ui/core/styles'

interface Props extends WithStyles<typeof styles> {
  // my props...
}

function MyComponent({ classes }: Props) {
  return (
    <div className={classes.root}>
      {/* my contents... */}
    </div>
  )
}

const styles = createStyles({
  root: {
    // my styles...
  },
})

export default withStyles(styles)(MyComponent)

と定義すると classes.xyz のように styles 変数の定義に存在しない ClassKey にアクセスしようとするとエディタが怒ってくれるようになります。 typeof stylesstyles オブジェクトを型の世界に持ち込んでいるのがみそですね。

関数形式の styles を利用する場合は以下のようにします。

const styles = ({ spacing }: Theme) => createStyles({
  root: {
    // my styles...
  },
})

始めは createStyles((theme: Theme) => ({ ... })) と定義しようと勘違いしておりました。

そして後から気づいたのですが、今回の内容は全部 https://material-ui.com/guides/typescript/ に書いてありましたね。

Material UI + TypeScript で Mixin を定義する

TypeScript + Materia UI でアプリを書いていて、テーマ関係のメソッドの中に createMixins というものを見つけたので使い方を調べてみました。

デフォルトの mixin といえば gutter くらいしかなくて、自分でも登録できたら色々使えそうだな...と思い、型定義を追いかけながらコードを試行錯誤したところ、多少不恰好ですが動くものができたのでメモしておきます。

プロジェクトコードは以下のリポジトリにあります。

プロジェクト作成

プロジェクトは create-react-appreact-scripts-ts で生成したものをベースにしています。この辺は特に詳細は説明しません。

create-react-app practice-mui-with-jsonschema-form --scripts-version=react-scripts-ts --use-npm

Theme の定義

Theme を定義しているファイルの全体のコードは以下のようになります。

import { createMuiTheme } from '@material-ui/core/styles'
import createMixins from '@material-ui/core/styles/createMixins'
import { CSSProperties } from '@material-ui/core/styles/withStyles'

// `Mixins` 定義を拡張する
declare module '@material-ui/core/styles/createMixins' {
  interface Mixins {
    myBackgroundMixin: CSSProperties, // <- custom mixin!
  }
}

// `createMixins` で利用するインスタンスを取得する
const { breakpoints, spacing } = createMuiTheme()

const mixins = createMixins(breakpoints, spacing, {
  myBackgroundMixin: {
    backgroundColor: '#ff0000'
  }
})

export default createMuiTheme({
  mixins,
  palette: {
    divider: '#ff0000'
  },
})

以下、個々のポイントを説明していきます。

Mixin の定義を拡張する

createMixins で参照されている interface Mixins の定義を見ていると何やら意味深なコメントが書いてあります。

export interface Mixins {
  gutters: (styles?: CSSProperties) => CSSProperties;
  toolbar: CSSProperties;
  // ... use interface declaration merging to add custom mixins
}

TypeScript の柔軟であり分かりづらい部分ですが、interface などの定義は後追い拡張(マージ)が行えます。 ここでは以下のように myBackgroundMixin を追加定義してみました。

declare module '@material-ui/core/styles/createMixins' {
  interface Mixins {
    // (=˘ ꒳ ˘=) 展開先を赤く染め上げる血みどろの Mixin...
    myBackgroundMixin: CSSProperties,
  }
}

Mixin の定義を行う

これにより createMixins の引数である MixinsOptions にも拡張が反映され、createMixins 内で myBackgroundMixin が定義できるようになります

const mixins = createMixins(breakpoints, spacing, {
  // (=˘ ꒳ ˘=) 血みどろの実装...容赦のないビビッドな赤
  myBackgroundMixin: {
    backgroundColor: '#ff0000'
  }
})

上の定義の拡張を行わないと Object literal may only specify known properties, and 'myBackgroundMixin' does not exist in type 'MixinsOptions'. というエラーが出ます。

ちょっと曲者な createMixins の引数

createMixins の引数には BreakpointSpacingインスタンスが必要なのですがいちいち用意したくはないですよね。今回は小手先のアイディアとして一度 createMuiTheme() を実行してデフォルトのインスタンスを取得しています。

const { breakpoints, spacing } = createMuiTheme()

もっといい方法がある気がしますね。

実際に利用してみる

上記の定義を含めた Theme インスタンスMuiThemeProvider 経由で提供してやれば、配下のコンポーネントで今回定義した myBackgroundMixin を利用することができます。

import * as React from 'react'

import Button from '@material-ui/core/Button'
import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles'

interface Props extends WithStyles<typeof styles> {
  name: string
}

function MyButton({ classes, name }: Props) {
  return (
    <Button className={classes.root}>
      {name}
    </Button>
  )
}

const styles = ({ mixins, palette }: Theme) => createStyles({
  root: {
    ...mixins.myBackgroundMixin,
    color: palette.common.white,
  }
})

export default withStyles(styles)(MyButton)

無事、手元では赤いボタンが表示されました。

ここまで、細切れの説明になりましたので、冒頭で紹介したリポジトリで全体を見ていただくのが分かりやすいかもしれません。