GAS「その操作を実行するには承認が必要です。」

Google Apps Scriptで毎日特定時刻にスクリプトを実行しているが、急に「その操作を実行するには承認が必要です。」のエラー通知が来るようになった。ちょうどGoogleアカウントのパスワードを変更した日だったのでそのせいもあるかもしれない。

Google Apps Scriptのページで実行してみると承認要求が出たので承認し、実行自体はできるようになるも、相変わらずトリガでの実行時にエラー通知が来る。「現在のプロジェクトのトリガー」を開くと以前とは違う画面(G Suite Developer Hub)へ飛ばされ、ここでおそらく「設定」のGoogle Apps Script APIがオフになっていたのが原因だった。

f:id:cactuaroid:20191227203922p:plain

f:id:cactuaroid:20191227203415p:plain

doubleを整数部と小数部それぞれを表すintから生成する時の速さ

関数の入力値の取りうるパターンが決まっているならあらかじめ計算しておくと速いよネタ。

Method Mean Error StdDev
string経由 223.3938 ns 2.5656 ns 0.1450 ns
小数部を計算して加算 53.5654 ns 8.8778 ns 0.5016 ns
小数部を計算して加算2 1.3613 ns 1.7037 ns 0.0963 ns
予め用意した小数部を加算 0.2498 ns 0.9411 ns 0.0532 ns

[C#]数値の桁数を調べる - Qiita

可変型のフィールドにreadonlyをつけてはいけないのか?

X DO NOT assign instances of mutable types to readonly fields.

A mutable type is a type with instances that can be modified after they are instantiated. For example, arrays, most collections, and streams are mutable types, but System.Int32, System.Uri, and System.String are all immutable. The read-only modifier on a reference type field prevents the instance stored in the field from being replaced, but it does not prevent the field’s instance data from being modified by calling members changing the instance.

Field Design | Microsoft Docs

An externally visible type contains an externally visible read-only field that is a mutable reference type.

The read-only modifier (readonly in C#, ReadOnly in Visual Basic, and const in C++) on a reference type field (or pointer in C++) prevents the field from being replaced by a different instance of the reference type. However, the modifier does not prevent the instance data of the field from being modified through the reference type.

CA2104:読み取り専用の変更可能な参照型を宣言しません - Visual Studio | Microsoft Docs

readonly付ければ再代入は防げるけど、それ自体が可変型だったら書き換えれてしまうからダメだよ、とのこと。これが妥当なガイドラインだとは思えない。後者はprivate以外のフィールドに限った警告なので若干理解できるが…。

readonlyが付いているフィールドにはその型のコンストラクタを抜けた後は代入されないことを保証できる。単にそれだけだ。可変型だったら書き換えられるというのは当たり前で、それはreadonlyとは別の話。可変か不変かという話とreadonlyの話を混ぜてはいけない。

  • 不変型を再代入不可なフィールドで扱いたい
  • 不変型を再代入可能なフィールドで扱いたい
  • 可変型を再代入不可なフィールドで扱いたい
  • 可変型を再代入可能なフィールドで扱いたい

これらは全てありうる。

例えば以下のように書くと、誤って型内部でListを再代入してしまうことを防げる。型内部でListが可変なのは誰が見ても分かるし、一方で型外部から見てもIReadOnlyListの中身が可変型である可能性は明らかであるので、メリットしかない。再代入を意図していないフィールドには全てreadonlyをつけておいた方が設計意図が明確になるのではないだろうか。

PrivateObjectExtensions 更新とNuGetで公開

ソースコードをコピペして回るのだるくなったのでdll配布するためNuGetで公開。使ってみてね!

www.nuget.org

cactuaroid.hatenablog.com

さくっとdll作って終わりかと思いきや、思わぬ伏兵がいた。元々Microsoft.VisualStudio.QualityTools.UnitTestFramework.dllのPrivateObject / PrivateTypeを拡張していたが、試しにVS2017でユニットテストプロジェクト作ってみたらこのdllが参照されておらず、代わりにMSTest.TestFrameworkのMicrosoft.VisualStudio.TestPlatform.TestFramework.Extensions.dllの中にいた。おそらく過去のVisual Studioで作ったユニットテストプロジェクトではMSTest V1として前者が使われていて、VS2017ではMSTest V2として後者が使われるように変更されたのだと思う。

困るのが、拡張メソッドを提供しているこのライブラリの依存先をどうすればいいのか問題。1つのソリューション中でも、ユニットテストプロジェクトの作成時期によってどちらを使っているかは分かれるが、それに応じてdllを分けるようなことも面倒なので避けたい。

幸いPrivateObject / PrivateType自体や名前空間は同じようなので、MSTest V1/2どちらにも依存せずに実行時に型を探すことで解決した。

dll, exeにビルド時のパスの情報が残らないようにする

dllやexeをReleaseビルドしても、アセンブリ内部にはビルド時のパスの情報が残っているのをご存じだろうか。試しにバイナリエディタで開いて、パスの一部でも検索してみてほしい。ビルド時のpdbファイルのパスが記録されている。

アセンブリを匿名で一般公開したつもりが、ビルド時のパスに名前が含まれていた、なんてことになると気持ち悪い。パスの情報が残らないようにするにはプロジェクトのプロパティを開いて、ビルドの詳細設定からデバッグ情報=「なし」に設定する。

f:id:cactuaroid:20190525155651p:plain

ただし、pdbファイルが必要ない時だけにすること。ビルド時のpdbファイルが残ってないと特にクラッシュダンプの調査に支障がでるので、製品コードでは困るでしょう。

C++でのやり方はこちら。

stackoverflow.com

Functional Programming in C# のメモ

本記事はFunctional Programming in C#を読んで私が重要と感じた部分を列挙したメモです。読みながらなるほどと思った点だけを列挙するため網羅性はありません。書籍の英語は平易でコードや解説も丁寧で読みやすいので興味ある方は是非書籍をお読みください。

Functional Programming in C#: How to write better C# code

Functional Programming in C#: How to write better C# code

感想

関数型プログラミング言語ではなく、C#関数型プログラミングをする方法が良くまとまっている。F#等の本だと文法の学習と関数型プログラミングの学習の両方が必要になり大変だが、よく知っているC#なので関数型プログラミングの学習だけに集中できた点が非常に良かった。

一方、関数型プログラミング言語に比べてC#では工夫を凝らす必要がある。型推論が不十分なので明示する必要があったりimplicit operatorの多用やクエリ式をモナド合成に使用するなどトリッキーに感じる部分もあり、C#アプリケーション全体を関数型プログラミングで記述するには障壁が大きい。部分部分ではビジネスシーンに活用できる知識もあるが、少なくとも各種モナドが公式で提供されてかつ関数型プログラミングのイディオムが言語仕様でサポートされるのを期待する。

たまたま流れてきたツイートに共感した。

https://twitter.com/skmruiz/status/1109452873170071552

PART 1 コアコンセプト

第1章 イントロ:関数型プログラミング

  • C#ではメソッドをFuncActionで取り扱えるので関数が第一級オブジェクトであり、高階関数を定義できる。
  • 例えばusing句の代わりに下記の高階関数を定義すれば重複コードを減らせる。定型コードで挟むような場合一般に適用できるパターン。
public static R Using<TDisp, R>(TDisp disposable, Func<TDisp, R> func)
    where TDisp : IDisposable
{
    using (disposable)
        return func(disposable);
}
  • 関数とは入力から出力へのマッピングである。したがってDictionaryも関数として使える。

第2章 純粋関数が重要なワケ

  • 並列処理や自動テスト結果の一貫性のため、関数は純粋であることが望ましい。
  • I/O処理は外界に依存するため絶対に純粋関数にできないが、I/O処理を分離して注入することでそれ以外を純粋にできる。
internal static void Run(Func<string, double> readDouble, Action<string> write)
{
    var height = readDouble("enter height in metres.");
    var weight = readDouble("enter weight in kg.");
    // ...
    write(message);
}

第3章 関数のシグネチャと型の設計

  • シグネチャに期待される通りの挙動をするのが良い。
  • intを引数として受けておいて条件でArgumentExceptionを投げるくらいなら、intではなく有効な範囲しかとらないデータ型を受けるべき。
  • 戻り値の有無によりFuncActionを使い分けなければならないのはコードの重複を生むので、戻り値voidの代わりにUnitを使うと便利。
  • Optionを返せば有効な値がないケースへの対処を強制できる。

第4章 関数型プログラミングのパターン

  • Mapはコンテナの中身の変形、Bindはコンテナ同士の結合。コンテナがIEnumerableならMap = SelectBind = SelectManyのこと。
  • Optionを要素数が0~1のIEnumerableと考えることで、OptionにもLINQと同じ関数を定義できる。
  • LINQと同じように、Optionでも変形やフィルタリングといった処理を通していき、最後はForEachやMatchなどで消費する。
  • functor(関手)とはコンテナ型越しにその内部のオブジェクトをMapできるコンテナ型のこと。monadモナド)とは同じようにBind(とReturn)できるコンテナ型のこと。OptionIEnumerableも関手でありモナドである。
  • 関数はマッピングの抽象レベルによって4つに分類できる:
関数のシグネチャ マッピングの種類
T -> R 低レベル間でのマッピング 普通の関数
A<T> -> A<R> 高レベル間でのマッピング Map, IEnumerable.Select()
T -> A<R> 低レベルから高レベルへ引き上げるマッピング Return
A<T> -> R 高レベルから低レベルへ引き下げるマッピング Reduce, IEnumerable.Aggregate()
  • 異なる抽象レベルを混ぜないことが重要。例えば高レベルの話の中にforループやnullといった低レベルの操作が混ざると効率的でなく不具合の元である。

第5章 関数合成によるプログラム設計

  • LINQでif文を使わないのと同じく、Optionでもif文を使わずにメソッドチェーンで実装する。LINQではIEnumerableで連鎖させるのでシーケンスでなければ扱えないが、Optionを使えば単一の値・オブジェクトを扱える。
  • 命令型では文で実装するが、関数型では式で実装する。(そのために式をつなげる=関数合成のためにOptionといったインターフェースが必要)

PART 2 関数型らしく

第6章 関数型のエラーハンドリング

  • OptionがNone(無効)とSome(有効な値)の二分だったのに対し、EitherはLeft(エラーの値)とRight(正常な値)の二分であり、LeftはNoneとは異なり値を持つことができる。これによりOptionよりも意味のあるエラーハンドリングが可能。
  • OptionEitherは非常に似ている。どちらも2本の線路とみなすことができる。
  • Eitherは汎用的なので、エラーハンドリング用に特化した型引数を持つ型を作る/利用するのが良い。
  • アプリケーション外部との界面で抽象レベルと実体レベルとを相互変換する。アプリケーションのコアでは抽象レベルで扱う。

第7章 アプリケーションを関数で組み上げる

  • 関数へ引数を段階的に与えるには、与えたい引数をキャプチャさせたクロージャとして新たなFuncに包んでいけばよい。複数の引数を持つ汎用関数から段階的に引数を与えて専用関数を得るということ。
public static Func<T2, R> Apply<T1, T2, R>(this Func<T1, T2, R> f, T1 t1)
    => t2
    => f(t1, t2);
// ...
  • ただし、C#コンパイラの制約上メソッドに対して直接Applyできないので、メソッドとしてではなくFuncに包んで定義しておく必要があることに注意。
  • Curryでカリー化することで、複数の引数を取る関数を単一の引数を取る関数がネストされた状態に変換しても、段階的に引数を与えられるが、Applyの方が直感的である。
public static Func<T1, Func<T2, R>> Curry<T1, T2, R>(this Func<T1, T2, R> f)
    => t1
    => t2
    => f(t1, t2);
// ...
  • OOPでDIする際はオブジェクトを渡すが、FPでは関数を渡す。アプリケーション全体をFPで組み上げるには、全てのモジュール間の依存関係を関数の注入で解決するイメージ。

第8章 複数の引数を取る関数を効果的に使う

  • 入力も出力もコンテナ型のままApplyできるコンテナ型をapplicative (functor)という。
    • 例えばOptionApplyは、T -> Rである関数をラップしたOption<T -> R>に対してOption<T>ApplyするとOption<R>を得られるというもの。
  • MapApplyReturnで定義できるし、ApplyBindReturnで定義できるので、MonadならApplicativeでありFunctorである。
パターン 定義されている関数 関数のシグネチャ
Functor Map F<T> -> (T -> R) -> F<R>
Applicative Return T -> A<T>
Apply A<(T -> R)> -> A<T> -> A<R>
Monad Return T -> M<T>
Bind M<T> -> (T -> M<R>) -> M<R>
  • メソッドチェーンによるLINQでは複数のシーケンスを扱う場合にネストになってしまい可読性が落ちる場合に、クエリ式を使うと可読性を向上させられる。同様に、複数のモナドを扱う場合の可読性を確保するためにクエリ式を活用できる。
  • 特に複数のfrom句のあるクエリ式ではselect句はSelectManyすなわちBindになるため、Bind操作の可読性が向上する。

※感想:クエリ式でフラットに書けるのでモナドの合成もネストされず良い、と書かれているがトリッキーすぎて可読性があるとは思えない。

第9章 データを関数的に考える

  • データを不変にするためには3つの方法がある。
    • 単に変更しないようにする
    • 不変型として定義する
    • 型をF#で書いてしまう
  • データ単体だけではなくデータ構造も不変にする必要があり、そのためにはSystem.Collections.Immutableの型を使うのが良い。

第10章 イベントソーシング:関数型の永続化アプローチ

(イベントソーシングの紹介なので未読)

PART 3 高度なテクニック

第11章 遅延実行・継続処理・モナドの合成

  • 遅延実行の基本は、値を渡すのではなくFuncに包んで渡すこと。
  • Funcを受け取りその実行前後で何かするという関数をモナドに包み合成可能にできる。(TryモナドMiddlewareモナド

第12章 ステートフルなプログラムと演算

  • ステートフルなプログラムを書くには、関数が状態を受け取り新しい状態を返すようにして、呼び出し元の関数のローカル変数で状態を持つようにする。

第13章 非同期演算

  • Task<T>は将来値が返るという効果を持ったコンテナ=モナドとして扱える。 ※大体Rxっぽくなる。

第14章 データストリームとReactive Extensions

(Rxの紹介なので流し読みしただけ)

第15章 メッセージパッシングの並列処理入門

(実用向けの内容のようなので未読)

誰のためのデザイン?のメモ

本記事は書籍「誰のためのデザイン?認知科学者のデザイン原論」を読んでメモしておきたかったことを箇条書きにしたものです。400ページほどありますが読みやすく事例も豊富で良い入門書なので、デザインを学ぶならとりあえず読んでおきたい一冊です。

誰のためのデザイン? 増補・改訂版 ―認知科学者のデザイン原論

誰のためのデザイン? 増補・改訂版 ―認知科学者のデザイン原論

  • デザイナーの持つ概念モデルを、システムイメージ(物理的な構造・文書)で表現して、ユーザーが同一の概念モデルを構築できるようにする。
  • 認知の3レベル…本能レベル・行動レベル・内省レベルすべてでデザインする。
    • 本能レベルで好まれる、見た目の良いものを。
    • 行動レベルで好まれる、思った通りに動作して、操作に対してフィードバックするものを。
    • 内省レベルで好まれる、使用感や結果を振り返った時に評価されるものを。
  • ユーザーが行為の七段階モデルに基づく質問に答えられるようにデザインする。
    • ゴール:何を達成したいか?
    • プラン:代替となる行為系列は何か?
    • 詳細化:今どの行為ができるのか?
    • 実行:それをどうやってやるのか?
    • 知覚:何が起こったのか?
    • 解釈:それは何を意味するのか?
    • 比較:それで良いか?私はゴールを達成したのか?
  • 強制選択機能は強力な制約であり、特に3つの手法がある。
    • インターロック:操作が適切な順序で行われることを強制する仕組み
    • ロックイン:よく考えずに何かをやめてしまうことを防止する仕組み
    • ロックアウト:何かをすることを防止する仕組み
  • エラーは2つに分類される。
    • スリップ:意図していない行動をとってしまった
      • 熟練者はタスクを無意識に行うことが多いため、スリップは初心者よりも熟練者に多く起こる
      • 共同追跡チェックリストによるスリップ防止:副操縦士がチェックリストを読み上げ、機長が実行する
    • ミステーク:行動は意図通りだが、そもそもゴール・プランが間違っていた
      • 初心者に多く起こる。
  • エラーに関するデザイン上のポイント
    • エラーを防ぐために制約を加える
    • エラーが起きてしまったときに元に戻せるようにする(アンドゥや自動保存からの復元)
    • 確認メッセージはスリップの防止には役立つがミステークの防止には役立たない
      • 操作対象となっているものをより目立たせるのが良い
    • 意味的妥当性チェック:普通はやらない操作を判断して警告する
    • 集中することを前提とせず、熟練した無意識的な行為であってもエラーしないよう、はっきりと区別可能にする
  • 理屈で作られた要求は常に間違っているし、必要なものを人に聞いて作った要求も常に間違っている。要求は、人が自然な環境にいるところを観察することで作られていくのである。