【MVEL入門】世界一のMVELerを目指して

注意：内容の正確性については保証できません。日本語の情報が無さすぎて孤独を感じている、MVEL初心者の心理的不安を少しでも和らげたいと思って書いているものなので、ご了承ください。

# MVELってどんな言語？
簡潔に表現すると、Javaをスクリプト言語にしたもの。
JavaScriptではなく、ScriptなJavaです。大事なことなのであえて言いました。

かなりマイナーな言語だとは思いますが、実はオープンソースです。
一応、定期的にissueとかもあがっていて、メンテもされているみたいですね。

- [GitHub](https://github.com/mvel/mvel)
- [公式ドキュメント](http://mvel.documentnode.com/)
- [Wikipedia](https://en.wikipedia.org/wiki/MVEL)

## 基本構文
```java
greet = "Hello "; // String型
name = "MVEL"; // String型
return greet + name; // 結果："Hello MVEL"
```

- 行末の区切り文字はセミコロン(;)
- 動的型付けなので、変数のデータ型宣言不要

MVELは最後に評価された値を戻り値として返す特性があります。
なので、returnは省略可能ですが、可読性を考えると付けておいた方が親切だと思います。

## 演算子

以下のような一般的な演算子はだいたい使えます。

- 比較演算子（==、!=、>、>=、<、<=）
- 算術演算子（+、-、\*、/、%、+=、-=、*=、/=、%=）
- 論理演算子（&&、||）
- etc...

他には、contains、is instanceof、~=などが使えます。

```java
// 文字列に「ん」が含まれているか判定
"こんにちは" contains "ん" //　結果：true

// 数値であるか判定
value = "100" // String型
value is instanceof Integer // 結果：false

// 半角英数字であるか判定
"Hello" ~= '^[A-Za-z0-9]+$' // 結果：true
```

| 演算子 | 説明 |
| --- | --- |
| contains | 左辺の値に右辺の値が含まれている場合true |
| is instanceof | 左辺の変数のデータ型が右辺のデータ型である場合true |
| ˜= | 左辺の値が右辺の正規表現とマッチする場合true |

## 空値（ヌルリテラル）について

```java
hoge == null;
hoge == nil; // nullと同義
```
MVELでは空値としてnullとnilが同義として扱われます。
どっちを使ってもいいですが、Javaに合わせてnullを使う方が無難かもしれません。

## 条件分岐
```java
// 年齢によって利用料金を変える
if (age >= 60) {
  System.out.println("シニア割引料金：1,200円");
}
else if (age >= 20) {
  System.out.println("一般料金：1,800円");
}
else {
  System.out.println("学割料金：1,000円");
}
```
条件式がtrueのとき、{}の中の処理が実行されます。
条件を複数設定したい場合は、else if()で繋げてください。

細かいことですが、else文は公式ドキュメントの書き方に沿って、改行を入れ一段下の行に書いています。

```java
age >= 15 ? "Yes" : "No";
```
このように三項演算子での条件分岐も可能です。

## 繰り返し処理
繰り返し処理には、基本的なループ構文（for、while、until、do while、do until）に加え、Java1.5で追加された拡張for文に似たforeachが使えます。

```java
// FizzBuzz問題
for (i = 0; i < 100; i++) {
  if (i % 15 == 0) {
    System.out.println("FizzBuzz");
  }
  else if (i % 3 == 0) {
    System.out.println("Fizz");
  }
  else if (i % 5 == 0) {
    System.out.println("Buzz");
  }
}
```
i++のように、インクリメントも問題なく使えます。

```java
// リストfruitsの要素を一つずつ取り出してコンソールに出力
fruits = ["りんご", "ぶどう", "パイナップル"];
count = 0;
foreach (item : fruits) {
  count++;
  System.out.println(count + ". " + item);
}
```

foreachでインデックスも表示させたい場合は、自分でインデックス用の変数を用意してあげないといけなさそうです。

## 関数定義

```java
// コンソールに文字列を出力（出力先が標準設定の場合）
def hello() {
  System.out.println("Hello, world"); // 結果：Hello, world
}

// 三角形の面積を計算し、メッセージを返す
function calcTriangleArea(bottom, height) {
  area = bottom * height / 2; // Int型
  message = "底辺が" + bottom + "㎝で高さが" + height + "㎝の三角形の面積は、" + area + "㎠です。"; // String型（変数は自動的にInt型 => String型へ変換）
  return message;
}
```

MVELの関数定義はdefとfunctionが使えます。
両者に違いはありませんが、公式ドキュメントではdefが使われているので、defに統一しましょう。

余談ですが、初めてMVELのソースを見たときにRubyと勘違いしました。

# 実践tips
## 日付を扱ってみよう
MVELで日付を扱うにはJavaのライブラリを使用します。
日付ライブラリにはDateとCalendarがありますが、今回は日時の演算処理を行いたいのでCalendarを採用します。

```java
import java.util.Calendar; // Calendarクラスをインポート

// カレンダーインスタンスを作成
Calendar calendar = Calendar.getInstance();
today = calendar.getTime(); // 現在日時を取得

calendar.set(2020, 4, 23, 23, 59, 59); // セール終了日を設定（2020年4月23日23時59分59秒）
endOfSaleDay = calendar.getTime();

if (today >= endOfSaleDay) {
  System.out.println("20%OFFセールは終了しました。");
}
```

上記は、割引セールが終了したかどうかを判断する処理です。
セール終了日の設定は、破壊的変更なので以降getTime()すると、設定した日時における現在日時になります。

# MVELerの定義
そもそもMVELerってなんやねん！と言われそうなので、言い出しっぺの責任として定義しておきたいと思います。
といっても、PHP使いならPHPer、Ruby使いならRubyistのような感じではあるのですが、厳密には以下の条件を満たすMVEL使いを指しています。

- ~~影響範囲の意識が十分にある者~~
- ~~デグレを起こさないよう積極的にコミュニケーションを取れる者~~
- MVELという言語を愛し楽しめる者

これらを満たせているMVEL使いは、**MVELerとしての覚悟**を持ち、胸を張ってMVELerを名乗りましょう。

# まとめ
- だいたいJavaにあるメソッドやライブラリが使える
- 使えば使うほど良さがわかってくる素晴らしい言語

# 参考
- [現場で使えるJavaライブラリ P527〜](https://books.google.co.jp/books?id=wbfV_-pJklUC&pg=PA527&lpg=PA527&dq=mvel&source=bl&ots=hGdP6OY4QO&sig=ACfU3U0L133yJbZpQ64HjSHgOlx2JSXAsg&hl=ja&sa=X&ved=2ahUKEwi_0L3HoqLpAhWDF6YKHZWSALQ4ChDoATAFegQIChAB#v=onepage&q=mvel&f=false)
- [MVEL2.0 Final リリースされたから使ってみました](http://yone098.hatenablog.com/entry/20081031/1225427372)
- [Droolsブログ : 07 MVEL](https://tokobayashi.hatenablog.com/entry/2019/08/28/174641)