さっき作った (@MakeNowJust)
foo = 42
p typeof(foo) # => Int32
foo = "foo"
p typeof(foo) # => String
if rand(0..1) == 0
foo = "foo"
else
foo = 42
end
p typeof(foo) # => Int32 | String
foo = 42
while true
case foo
when Int32 then foo = "foo"
when String then break
end
end
p typeof(foo) # => String
分岐・ループなど
様々なパターンがあって
かなり複雑!!
def foo(x, y)
p x + y
end
foo 1, 2 # => 3
foo "foo", "bar" # => "foobar"
# foo :foo, :bar # raises compilation error!!
メソッドの呼び出しがあったら
実引数の型をとりあえずあてはめてみて
コンパイルができるかを確かめている。
(型制限のない引数の型はC++のテンプレート的な)
class Foo
def foo
@foo
end
end
p Foo.new.foo
`@foo`に代入せず
いきなり参照している
(Rubyだとこの場合`nil`が返る)
A. コンパイルするから。
コンパイルするために型を決定することは必須ではないが
最適化を上手く適用したり、高速なコードを生成するには
型を決定することが必須。
The type of a instance variable, if not declared explicitly with
`@foo : Type`, is inferred from assignments to it across
the whole program.
The assignments must look like this:
1. `@foo = 1` (or other literals), inferred to the literal's type
2. `@foo = Type.new`, type is inferred to be Type
3. `@foo = Type.method`, where `method` has a return type
annotation, type is inferred from it
4. `@foo = arg`, with 'arg' being a method argument with a
type restriction 'Type', type is inferred to be Type
5. `@foo = arg`, with 'arg' being a method argument with a
default value, type is inferred using rules 1, 2 and 3 from it
6. `@foo = uninitialized Type`, type is inferred to be Type
7. `@foo = LibSome.func`, and `LibSome` is a `lib`, type
is inferred from that fun.
8. `LibSome.func(out @foo)`, and `LibSome` is a `lib`, type
is inferred from that fun argument.
Other assignments have no effect on its type.
`src/compiler/crystal/semantic/type_guess_visitor.cr`に
今回説明した辺りの実装がある。
(この`TypeGuessVisitor`はクラス変数の型推論にも使われる)
型推論ではなくて、型推測なのでは?
‥‥それな!!