Concurrent Clean: 一意型と正格let

先ほどMLに流れたメールに衝撃を受けていろいろ試しているのですが、
とりあえず、

swap arr i j
    #! x = select arr i
       y = select arr j
    = update (update arr i y) j x

という関数は正しく型付けされるということです。
しかし、

swap arr i j
    # x = select arr i
      y = select arr j
    = update (update arr i y) j x

これはだめです。
この理由はここに書いてあるのですが、
http://sky.zero.ad.jp/~zaa54437/programming/clean/LanguageReport21/Chap9.html#sc4
この正格letの型付けに対する影響を知らなかったのでびっくりしているところです。

      • -

あー。この機能、使えるような使えないような・・・
とりあえず、配列に対しては使えることは分かりました。実際使えそうです。
*File型に対しても、同じ効果があるのですが、対応する関数がないのであまり意味がありません。
ユーザー定義型については・・・

definition module Util

import StdEnv

::R a = R .{a}

createR :: !*{a} -> *R a
selectR :: !.(R a) !Int -> a
updateR :: !*(R a) !Int !a -> *(R a)

============================================
module Main

import StdEnv, Util

g :: *(R a) Int a -> (a,*(R a))
g r i v
    #! w = selectR r i
    #! arr = updateR r i v
    = (w,arr)

Start :: (Int,*R Int)
Start = g (createR {1,2,3,4,5}) 2 10

こういうのなら大丈夫なのですが、

::R a

という風に抽象定義型にしてしまうとそれだけでダメです。

      • -

あと、式が関数型になる場合もダメですね。

::R a = R .{a} (Int -> a)

g :: .(R a) -> (Int -> a)
g (R _ f) = f

updateR :: *(R a) Int a -> *(R a)

f r i v
    #! h = g r
       r = updateR i v
    = (h,r)

みたいなのは、hの型が「Int -> a」になるのでだめです。まあ、これを許すといろいろ悪用できるのでだめなのは仕方ないですが。

      • -

関数型だけじゃなくて、型変数が裸で現れない場合はすべてだめみたい。
次のような例もだめ。

::P a b = P a b
::R a b = R .{a} (P a b)

createR :: a b -> *R a b
createR a b = R {a} (P a b)

selectR_a :: .(R a b) -> a
selectR_a (R a _) = a.[0]

updateR_a :: *(R a b) a -> *(R a b)
updateR_a (R a p) v = R (update a 0 v) p

trickR :: *(R a b) -> (P a b)
trickR (R a p) = p

f :: *(R a b) a -> (P a b, *R a b)
f r a
    #! p = trickR r
       r = updateR_a r a
    = (p,r)

Start = f (createR 1 'a') 10