X



Closures vs Objects
0001デフォルトの名無しさん
垢版 |
2024/02/19(月) 20:39:15.91ID:VT95BnI9
It is well-known that closures and objects are equivalent. So are actors. Which one is the most fundamental?

よく知られたように、クロージャとオブジェクトは等価です。アクターも同様です。どの概念が最も基本的なのでしょうか?
0002デフォルトの名無しさん
垢版 |
2024/02/19(月) 20:49:06.93ID:VT95BnI9
A closure is a function with state in the local scope.
An object also has methods and state which belong to its instance.
These are compatible with each other.

クロージャはローカル・スコープに状態をもつ関数です。
オブジェクトもまたそのインスタンスに属するメソッドと状態を持ちます。
これらは互いに互換性があります。
0003デフォルトの名無しさん
垢版 |
2024/02/20(火) 09:36:12.34ID:fQy0X2aE
オブジェクトは複数のメソッドを持てる

クロージャは関数をひとつしか返せない

オブジェクトの勝ち
0004デフォルトの名無しさん
垢版 |
2024/02/20(火) 11:01:18.21ID:BFMbzAij
>>3
ハッシュテーブルに関数を追加して返すことで、複数のメソッドを返すことが可能
より柔軟に、オブジェクトとそれを操作する関数の組を返すこともできるし、無限個のメソッドをもったオブジェクトだって作れる
これらは書く対象によって使い分ければよいのであって、オブジェクトにメソッドが属しているのが常に最適とは限らない
たとえばイベントハンドラにコールバック関数として渡すような場合は、ひとつの関数として取得できたほうが便利
0005デフォルトの名無しさん
垢版 |
2024/02/20(火) 23:29:16.13ID:0cXn/136
クロージャのほうがprimitive
今時、高階関数やイテレータを書くのにわざわざデザインパターンとか使いたいかって話
クラスは、レキシカル環境つきオブジェクトだけが欲しくても必ず新しい名前空間が作られるし、単品で購入できない福袋みたいなもん
0006デフォルトの名無しさん
垢版 |
2024/02/21(水) 05:55:14.17ID:j79lZ4PO
二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然
オブジェクトと引数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
0007デフォルトの名無しさん
垢版 |
2024/02/21(水) 05:56:37.93ID:j79lZ4PO
二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然
オブジェクトと関数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
0008デフォルトの名無しさん
垢版 |
2024/02/21(水) 10:09:33.92ID:HBySQ9lj
2変数関数をクロージャにラップして1変数関数にしても、メソッドチェーンはできないよね
0009デフォルトの名無しさん
垢版 |
2024/02/21(水) 10:29:18.43ID:Cg6r5DK9
[1, 2, 3, 4, 5]にメソッドが属してれば

[1, 2, 3, 4, 5]
.filter(hoge) # ←戻り値もオブジェクト
.map(piyo) # ←これも
.sum()

みたいにできるけど、配列がオブジェクトじゃなかったら

map([1, 2, 3, 4, 5], hoge)

の戻り値はただの配列なので、繰り返し関数適用するには、Lispみたいな醜い入れ子にするしかない

sum(
 map(
  filter([1, 2, 3, 4, 5], hoge),
 piyo))
0010デフォルトの名無しさん
垢版 |
2024/02/21(水) 12:32:59.23ID:pWazjr0Z
>>8-9
これでどうよ

chain = (obj) => {
 return (f, ...args) => {
  if (!f) return obj
  return chain(f(obj, ...args))
 }
}

chain([1, 2, 3, 4, 5])(
 filter, (x) => x % 2)(
 map, (x) =>x * x)(
 sum)()
// => 35
0011デフォルトの名無しさん
垢版 |
2024/02/21(水) 15:43:45.17ID:im/wtuEl
一時期メタプログラミングとかいって、動的にクラスやメソッドを作れるのがすごいって言われてたけど、クロージャをデータ構造として使うならそれは自明にできることでは
0013デフォルトの名無しさん
垢版 |
2024/02/21(水) 22:54:29.50ID:DX/jvS2m
入れ子にしなくても関数合成でいいのでは?
メソッドチェーンっぽく書くには
Reverse-application operatorってのもあるけど
個人的には関数合成のほうがスッキリしててすこ

https://ideone.com/gCWxLP
let hoge n = n mod 2 = 1
let piyo = ( * ) 2
let () = print_int (List.fold_left (+) 0 (List.map piyo (List.filter hoge [1;2;3;4;5])))

(* function composition *)
let (<<) f g x = f (g x)
let f = print_int << List.fold_left (+) 0 << List.map piyo << List.filter hoge
let () = f [1;2;3;4;5]

let (>>) f g x = g (f x)
let f = List.filter hoge >> List.map piyo >> List.fold_left (+) 0 >> print_int
let () = f [1;2;3;4;5]

(* @@ Application operator *)
let () = print_int @@ List.fold_left (+) 0 @@ List.map piyo @@ List.filter hoge [1;2;3;4;5]

(* |> Reverse-application operator *)
let () = [1;2;3;4;5] |> List.filter hoge |> List.map piyo |> List.fold_left (+) 0 |> print_int
0014デフォルトの名無しさん
垢版 |
2024/02/22(木) 01:30:07.21ID:seCGgTJE
Forth で書いてよ
0016デフォルトの名無しさん
垢版 |
2024/02/22(木) 08:40:34.15ID:GWomKHpC
chainl = (obj) => {
 return (f, arg) => {
  if (!f) return obj
  return chainl(f(arg, obj))
 }
}

cons = (x, xs) => [x, xs]

foreach = (lst, f) => {
 if(!lst) return
 [x, xs] = lst
 f(x)
 foreach(f, xs)
}

linkedList =
 chainl(null)(
  cons, 1)(
  cons, 2)(
  cons, 3)()

chainl(linkedList)(foreach, console.log)()
// 3
// 2
// 1

いろいろ書けるんだな
0017デフォルトの名無しさん
垢版 |
2024/02/22(木) 10:55:42.37ID:2PGen9gt
カリー化もクロージャと可変長引数があればできるね

currying = (f, ...args) => (...rest) => f(...args, ...rest)
0018デフォルトの名無しさん
垢版 |
2024/02/22(木) 11:04:45.60ID:2PGen9gt
これで、mapなどが

map(f, col)

と、関数を先にとる形でも、>>16のchainlを用いて

chainl([1, 2, 3, 4, 5])(
 currying(filter, (x) => x % 2))(
 currying(map, (x) => x * x))(
 sum)()

と書けるわけか
0019デフォルトの名無しさん
垢版 |
2024/02/22(木) 11:11:29.91ID:2PGen9gt
いや、単にchainlを

chainl = (obj) => {
 return (f, ...arg) => {
  if (!f) return obj
  return chainl(f(...arg, obj))
 }
}

とすればいいのか
0020デフォルトの名無しさん
垢版 |
2024/02/22(木) 12:04:47.68ID:0KLGqPZR
クロージャの真価はコンテキストを導入できること

(let ((a 1) (b 2)) (+ a b))

((lambda (a b) (+ a b)) 1 2)

は等価
なので、上のa + bの変わりに関数を返却すれば独立したスコープの状態をもつ何かを作れる

クラスベースのオブジェクト指向でも同様だが、そのようなコンテキストのテンプレート(= class)は静的にしか作れない
0021デフォルトの名無しさん
垢版 |
2024/02/22(木) 12:15:29.98ID:Q3haYGVO
静的なほうがバグの発見は早そう
0022デフォルトの名無しさん
垢版 |
2024/02/22(木) 13:03:51.54ID:BdBf3uc1
なんでも自由を許すと無秩序になる
>>10だって、シグネチャを無視することが前提の設計になってる
(最後に結果を呼び出す時は第一引数のfを省略する)

TypeScriptが使われるのもそれが理由
今やPythonやRubyにも型がある
0024デフォルトの名無しさん
垢版 |
2024/02/22(木) 13:24:08.05ID:WrtsfcL/
型なんて、プログラマがちまちま書かずとも、コンパイラや型チェッカが実行前に検査してくれればいいと思う
0025デフォルトの名無しさん
垢版 |
2024/02/22(木) 15:02:53.21ID:bbu+R1aP
currying = (f) => {
 args = []
 apply = (a) => {
  if (a === undefined) return f(...args)
  args.push(a)
  return apply
 }
 return apply
}
0026デフォルトの名無しさん
垢版 |
2024/02/22(木) 18:39:34.82ID:SfbO+Gz+
クロージャはポリモーフィズムができない
オブジェクト指向言語ではたとえば比較演算子の動作を型によって変えられる
a.compare(b)とソースコードのどこに書いても、aの型に応じて適切にcompareの動きが選択される
変数の型によって実行する関数を変えるにはどうする?
まさか関数定義に型による条件分岐をいちいち書くわけじゃあるまいし
0027デフォルトの名無しさん
垢版 |
2024/02/22(木) 19:07:57.79ID:v9QmWW2c
class構文が入るまでどうやってJSでクラスを定義してたのか調べれば分かるよ
0028デフォルトの名無しさん
垢版 |
2024/02/22(木) 19:20:20.77ID:bu5i+qrs
>>27
a.hoge()のようにしてダックタイピングするなら当然できる
hogeがa(or a.prototype)に属さないただの関数の場合でも
hogeの中で型による条件分岐を書く以外の方法でできるか
0030デフォルトの名無しさん
垢版 |
2024/02/22(木) 19:41:55.54ID:v9QmWW2c
>>28
なんて?
0031デフォルトの名無しさん
垢版 |
2024/02/22(木) 20:46:51.05ID:5LGf2QVl
>>26,28
a, bではなくcompareのほうを、状態をもった関数オブジェクトとして生成する

一例として、

compareのクロージャには、「型名=>その型の引数を受け取った時に呼び出す関数」のテーブルを保持する
そのテーブルに関数を動的に登録するための関数を作成する

例:
IComparable = () => {
 dispatch = {}
 compare = (self, other) => { /*dispatchテーブルから関数を検索して実行*/ }
 implement = (type, fn) => { /* dispatchテーブルにfnを登録 */ }
 return [compare, implement]
}

もちろんdispatchの条件は型名じゃなくても、別の判定用関数を用意してもいい
0032デフォルトの名無しさん
垢版 |
2024/02/22(木) 20:49:14.36ID:5LGf2QVl
インタフェースの実装は、型定義に書くよりも使用時に動的にできた方が自然だ
たとえば木構造にイテレータを実装するとして、深さ優先探索にするのか幅優先探索にするのかは、その時々によって変わる。木構造の定義だけからは決まらない
0033デフォルトの名無しさん
垢版 |
2024/02/22(木) 22:14:42.54ID:BzyFji/L
最近のフレームワークはどんどん脱オブジェクト指向・関数型化してないか?
Pythonのフレームワークは機能のMixinは継承ではなくデコレータ(ようはクロージャ)によるものが多い
Reactなんか思想は元々関数型的だったけど、ついにクラスコンポーネントやめちゃった
0034デフォルトの名無しさん
垢版 |
2024/02/23(金) 00:14:17.41ID:vwgp24xa
cps = (f) => (...args) => args.pop()(f(...args))

eq = cps((a, b) => a === b)
sub = cps((a, b) => a - b)
mul = cps((a, b) => a * b)

fact = (n, k) => {
 eq(n, 0, (isZero) => {
  if(isZero) {
   return k(1)
  }
  else {
   return sub(n, 1, (prevn) =>{
    fact(prevn, (prevf) => {
     mul(n, prevf, k)
    })
   })
  }
 })
}

fact(5, console.log)
0035デフォルトの名無しさん
垢版 |
2024/02/23(金) 13:52:03.15ID:P8ZW/vvm
Closures are functions with contexts.
Objects are structures with contexts.
A structure can have functions as its member, and vice versa.
So both are equal.
0037デフォルトの名無しさん
垢版 |
2024/02/24(土) 00:22:58.31ID:PF21lJOY
>>36
パフォーマンスは?
0038デフォルトの名無しさん
垢版 |
2024/02/24(土) 06:28:09.62ID:dndVDS6q
>>37
1. ハードウェア/コンパイラの技術向上に期待する
2. ネックになる部分だけ最適化する
3. そもそもパフォーマンスが必要ならC++やRustなどで書く
0039デフォルトの名無しさん
垢版 |
2024/02/24(土) 11:30:44.99ID:PF21lJOY
>>38
その回答、第一級継続関係無いやん。
0040デフォルトの名無しさん
垢版 |
2024/02/24(土) 21:14:52.39ID:4BGFiybC
オブジェクトとクロージャが使えることは、コードブロック(+ ローカル変数)を第一級オブジェクトに持っているのだと思える
継続が第一級オブジェクトなのは、プログラム実行時の任意の状態が第一級オブジェクトということ
0041デフォルトの名無しさん
垢版 |
2024/02/24(土) 21:42:30.15ID:fbMBQzh4
>>40
継続使う場合のパフォーマンスは?
0043デフォルトの名無しさん
垢版 |
2024/02/25(日) 09:10:18.70ID:zl+9FS4I
継続はクロージャで表現されるけど、オブジェクトとクロージャが同値なら、オブジェクトによる表現もできるの?
0044デフォルトの名無しさん
垢版 |
2024/02/25(日) 10:52:31.35ID:5QMstwYR
: fact dup
1 = if
    drop
  else
    dup 1- rot rot *
    swap recurse
  then
;

1 5 fact cr .
0046デフォルトの名無しさん
垢版 |
2024/02/25(日) 11:17:17.62ID:azDE+Je3
近年まれにみる良スレ
0048デフォルトの名無しさん
垢版 |
2024/02/25(日) 16:27:53.33ID:4fScDWQR
ハァ?
0049デフォルトの名無しさん
垢版 |
2024/02/25(日) 16:36:21.69ID:wVCQJTWx
>>47
使いにくい上に動的ディスパッチとなっている
継承の問題点を解消している好例はRust
その上で使いやすく静的ディスパッチによる単相化も可能で速い
0050デフォルトの名無しさん
垢版 |
2024/02/25(日) 17:38:12.08ID:EvE7ghxV
>>49
どのへんが使いにくいのかがわからないし、動的ディスパッチだと何がよくないのかもわからない

Rustのほうが早いのはそりゃ当たり前
0051デフォルトの名無しさん
垢版 |
2024/02/25(日) 17:55:13.30ID:BE8i65fG
>>43
できるだろうけど、継続自体が手続き/関数的な概念なので、オブジェクトによる表現をするメリットは少ないと思う
0053デフォルトの名無しさん
垢版 |
2024/02/25(日) 20:50:19.80ID:ZY2lsWgE
>>52
継続が第一級なら保存した継続を何度も呼び出せるが、コルーチンはyieldしたらもうその時点には戻れないので、継続のほうが根本的
0054デフォルトの名無しさん
垢版 |
2024/02/26(月) 12:34:53.26ID:9feDQZG5
なんでもクロージャだと戻り値全部関数だけど、型情報を持つことはできる?
ディスパッチするのに必要だよね?
0055デフォルトの名無しさん
垢版 |
2024/02/26(月) 15:19:09.55ID:L4EYeETV
>>54
type属性をもったオブジェクトとして返却すればいいんじゃないんですかね
それを毎回書きたくないなら、生成用関数を受け取って新しい生成用関数を返す関数

makeConstructor = (f) => (type, ...args) => {type: type, value: f(...args)}

みたいなのを使ってみてはどうでしょうか
0056デフォルトの名無しさん
垢版 |
2024/02/26(月) 18:51:01.76ID:k7j7cG9/
>>55
これ、オブジェクトからtype属性つきオブジェクトへの関数、を持ち上げる関数書けばモナドになるから自動的に変換できるんだな
0057デフォルトの名無しさん
垢版 |
2024/02/26(月) 22:39:42.49ID:qQrR5NbL
そもそもオブジェクトの型を動的に判定することは不可能なのだ
ただのシンボル'0'が整数であるとは数学的に決して言えない
整数の性質を満たす集合に属しているから整数と言えるのだ
だからオブジェクトが型を授かるためにクラスは絶対に必要なのだ
0058デフォルトの名無しさん
垢版 |
2024/02/26(月) 22:55:13.16ID:9WvTd710
そもそもふだんそんなにポリモーフィズムしてるか?
型ヒント書いたら実行前にチェックできるとか、中間コードにコンパイルされる時高速になるとか、それでよくないか?
0059デフォルトの名無しさん
垢版 |
2024/02/26(月) 22:58:38.34ID:sdXywFcL
モダンなプログラミング言語
Go、Rust、Zig、Nim、Julia、Elixirなどは
クラスおよびその継承を言語仕様から排除している
それぞれの言語は全く異なる方針を採っているがクラス排除だけは全てで一致している
クラスとその継承は悪手であるとプログラミング言語界では結論が出ている
0060デフォルトの名無しさん
垢版 |
2024/02/26(月) 23:36:19.27ID:L97i44S4
xの型がT
y = Foo(x)なら
yの型はFoo(T)

というシステムでいいと思うの

Length ::= Float
Angle ::= Float
Point ::= Tuple(Float, Float)
Triangle ::=
Triangle(Length, Length, Length)
| Triangle(Length, Length, Angle)
| Triangle(Length, Angle, Angle)
| Triangle(Point, Point)
0062デフォルトの名無しさん
垢版 |
2024/02/27(火) 00:02:52.99ID:woInknqq
クラスは不要で型クラスは必要
0063デフォルトの名無しさん
垢版 |
2024/02/27(火) 06:56:00.52ID:cCaBYlch
Natural = fn
 [0] 0
 [n: n >= 0] n
end

succ = fn [Natural(n)] Natural(n + 1) end

add = fn
 [n, Natural(0)] n
 [n, succ(m)] succ(add(n, m))
end

mul = fn
 [n, Natural(0)] Natural(0)
 [n, succ(m)] add(mul(n, m), n)
end

AdditiveMonoid = Monoid()
AdditiveMonoid.implimentsIdentity(Natural, fn [] Natural(0) end)
AdditiveMonoid.implimentsAppend(Natural, add)

MultiplicativeMonoid = Monoid()
MultiplicativeMonoid.implimentsIdentity(Natural, fn [] Natural(1) end)
MultiplicativeMonoid.implimentsAppend(Natural, mul)
0064デフォルトの名無しさん
垢版 |
2024/02/27(火) 11:48:35.86ID:T+tKpG5+
たしかに「この関数から返ってたらこの型」というのはそうか
インタフェース型を返す関数から返った値は、その実体がなんであってもインタフェース型として受け取らないといけないのか
0065デフォルトの名無しさん
垢版 |
2024/02/27(火) 14:07:25.42ID:HfXhKUnc
Pythonの型ヒントも、内部的にやってることは型の階層構造を保存したオブジェクト作ったりしてるだけだし、型をクロージャで表すというのは十分に現実的
0066デフォルトの名無しさん
垢版 |
2024/02/28(水) 09:15:01.79ID:ZVd213Rl
なんでもクロージャでは構造に基づくサブタイピングができないから、ポリモーフィズムするなら必然的に名前に基づくサブタイピングをする必要がある
0067デフォルトの名無しさん
垢版 |
2024/02/28(水) 18:59:36.77ID:FE9gquBK
型注釈はクロージャで実現できるけど、肝心の個別のオブジェクトに型を付与するには、処理系に手加えないと無理だね
0068デフォルトの名無しさん
垢版 |
2024/02/28(水) 20:20:50.87ID:YGaU0GOk
ただのクロージャにこれはLinkedListだみたいな型情報をつけるなら

JavaScriptのようにすべてのオブジェクトがフィールドを持てるようにして、クロージャもtype属性みたいなのを持てるようにする

Pythonのように__call__()メソッドを実装すればオブジェクトも関数として呼び出せるようにして、オブジェクトにラップする

おとなしくにハッシュテーブルにラップするとかして、関数としてのインタフェースを保つのは諦める
0069デフォルトの名無しさん
垢版 |
2024/02/28(水) 22:56:39.74ID:E1vk9WfJ
型定義

Counter = Type(fn [n]
 fn [i] n += i end
end)
f = Counter(0)
f is Counter # true
f(1) # 1
f(2) # 3
f(3) # 6


エイリアス

Adder = Counter
f is Adder # true


多相型

Pair = Type(fn [T, S]
 fn [t: T, s: S] (t, s)
end)

p = Pair(Int, Str)(1, 'Hello')
p is Pair(Int, Str) # true
0071デフォルトの名無しさん
垢版 |
2024/02/29(木) 09:14:52.93ID:kNys7UfZ
実行のたびに無名関数を生成すると
Pair(Int, Str)の実体が毎回変わってしまう
同じ型として扱うならキャッシュしとかんといかん


Foo()の結果がFoo型になるので、自然にヒエラルキー

Pair(Int, Str) ⊂ Pair ⊂ Type

ができる


子はすべての親を知っているが、親は直接の子しか知らない
0072デフォルトの名無しさん
垢版 |
2024/02/29(木) 21:57:07.71ID:FO1fpwyp
ユニオン型

Male = Type()
Female = Type()
Sex = Male | Female


代数的データ型

Nil = Type()
Cons = Type(fn
 [T] fn [x: T, xs: List(T)] (x, xs) end
end)
List = Type(fn [T] Nil | Cons(T) end)

c = Cons(Int)(1, Nil())
c is Cons(Int) # true

nl = Nil()
nl is Nil # true
nl is List(Int) # ( = Nil | Cons(T) ) true

l = Cons(2, c)
l is List(Int) # true
0073デフォルトの名無しさん
垢版 |
2024/03/01(金) 09:17:33.48ID:uT9YQz7T
クロージャは

(1) 評価可能な任意のコードブロックをオブジェクト化できる
(2) 新しいコンテクストを導入できる
(3) 評価を遅延可能
(4) 即時生成可能
0074デフォルトの名無しさん
垢版 |
2024/03/01(金) 13:47:12.10ID:hUzmZgl8
クロージャは、評価可能な任意のコードブロックを第一級オブジェクトとして扱える

継続は、プログラム実行時の任意の状態と処理を第一級オブジェクトとして扱える
0075デフォルトの名無しさん
垢版 |
2024/03/01(金) 15:52:11.47ID:5dJkdiP3
欠点は何?
0078デフォルトの名無しさん
垢版 |
2024/03/02(土) 00:04:51.39ID:WOl8O7ii
言語の高速化は処理系の仕事なのだから、特定の処理系で実行速度を最適化するために冗長な書き方を強いられるのは馬鹿げている
0080デフォルトの名無しさん
垢版 |
2024/03/02(土) 01:38:33.15ID:vVlrIC3U
>>76
そこはシンタックスシュガー用意できる?
0083デフォルトの名無しさん
垢版 |
2024/03/02(土) 11:12:48.28ID:hFGO4sAk
決定性のバックトラックなんてあるの?
0084デフォルトの名無しさん
垢版 |
2024/03/02(土) 16:06:45.66ID:BdR3F6bT
>>81
Pythonでいうとこのジェネレータ内包表記みたいなのがあれば、多くの場面で十分なのかな
0085デフォルトの名無しさん
垢版 |
2024/03/02(土) 19:52:13.99ID:IQAkxugo
>>84
コルーチンの中でコルーチンを呼び出したら、yieldしたら最上位の呼び出し元まで返ってくるのが望ましい
0086デフォルトの名無しさん
垢版 |
2024/03/02(土) 21:01:52.57ID:yMdn9rGJ
>>85
非同期コルーチンAの中で非同期コルーチンBを呼ぼうとしたら自動的にAはyieldして戻ってからコルーチンBを呼び出し
そのBについても全て終えるか他コルーチン呼び出しで自動的にyield
それぞれyieldして待機中の非同期コルーチンは呼び出し先が解決するとyield時点から再開
というスタックレス非同期コルーチンが何万も稼働しています
0088デフォルトの名無しさん
垢版 |
2024/03/03(日) 14:01:49.17ID:IZl/xWS6
>>87
スタックレス非同期コルーチンは上位ランタイムがCPUコアスレッド数をフルに使って幾万個の軽い非同期コルーチンをスケジューリングすることでCPU性能を使い尽くすことができる
0092デフォルトの名無しさん
垢版 |
2024/03/05(火) 13:01:59.74ID:hcPpBjWs
スタックフルのコルーチンは、内部で呼び出したコルーチンがyieldしてるかどうか呼び出し元が知っていないといけない
つまりカプセル化を破壊する
0093デフォルトの名無しさん
垢版 |
2024/03/05(火) 14:58:20.75ID:QuH/Int2
複数のスレッドが協調して何かするなんてのは、もはやプログラミング言語の領分じゃないんだよなぁ
0097デフォルトの名無しさん
垢版 |
2024/03/13(水) 21:40:17.77ID:lx8X47UA
関数とオブジェクトなんて、要はどっちも値を入れたら何かを返す箱なのだから、内部構造を考えなければ共通化できるはず
配列も、機能だけ見れば、キーが数値なだけのただのオブジェクト
リストやジェネレータなどをイテレートするのも、関数を引数なしで呼び出すようなもの
0098デフォルトの名無しさん
垢版 |
2024/03/13(水) 23:38:55.71ID:+S4Brq3S
Pythonは__call__()メソッドを実装することでオブジェクトも関数のように振る舞える
これは小規模なプログラムでは関数として実装しておき、大規模になったらシームレスに移行できるように
0099デフォルトの名無しさん
垢版 |
2024/03/14(木) 18:36:51.37ID:rbf7F1li
Iterable⊂Callable⊃Associative

だが、Iterableとしての呼び出し方とAssociativeとしての呼び出し方が異なる
0100デフォルトの名無しさん
垢版 |
2024/03/14(木) 18:40:46.10ID:rbf7F1li
x = [1, 2, 3]をIterableとしてCallableだと思う場合、x() = next(x)
AssociativeとしてCallableだと思う場合、x(n) = x[n]
になる
0101デフォルトの名無しさん
垢版 |
2024/03/14(木) 21:09:23.66ID:xE50NtDY
マシン語を知らないと妙なこだわりを言い出すからおもしろいよなw
0102デフォルトの名無しさん
垢版 |
2024/03/14(木) 23:24:02.50ID:yxqrN1vV
異なる層の話を出すのもどうかと思うがね
0103デフォルトの名無しさん
垢版 |
2024/03/15(金) 08:26:32.50ID:vBMVv6T3
>>101
コンピュータサイエンスにおいて低レイヤーが上位という価値観があるのって、おそらく日本だけだと思うんだけど、どうしてこういう逆行現象が起こるんだろう?
0104デフォルトの名無しさん
垢版 |
2024/03/15(金) 08:47:50.82ID:0zJIPs3S
数学だと基礎論は馬鹿にされてるよね
通常の数学やるときに選択公理だとか不完全性定理だとかいってるのは素人だけ
そりゃ形式的に「基礎論のほうが原理に近い」というだけならべつに数学を知らなくても分かるからね
0105デフォルトの名無しさん
垢版 |
2024/03/15(金) 08:56:58.18ID:wSiNosZi
>>103
マシン語わかる
でもコンピューターサイエンスの素養なし
って感じのロートルが必至にマウントとろうとしてるだけだろな
0106デフォルトの名無しさん
垢版 |
2024/03/15(金) 09:01:28.78ID:0zJIPs3S
ふつうは情報学科でも昔ならSICPでやったようなプログラミングの概念や機能に関する講義があるはずで、
ちゃんと勉強している人で実装と機能を分けて考えるということができない人はいないよ

日本で低レイヤーが偉そうな顔してるのは単に、大学等で体系的に学ばずに昔からパソコンに触ってただけのおじさんが言ってるだけ
日本ではプログラマが高等教育が必要な専門技術職として見なされておらず、パソコンしか取り柄のない社会不適合者を低賃金でこきつかう職種だと見なされていたのが原因
0107デフォルトの名無しさん
垢版 |
2024/03/15(金) 09:10:45.64ID:5Fl9dqIV
>>104
物理学科の自称秀才がやたらと素粒子論専攻したがるのもこれだよなぁ……
実際に勉強して内容に興味を持てたかどうかではなくて、ラベルで判断してる
20過ぎた大人が将来の進路に関わる選択でそういうことするのは、病的としか言いようがない
0108デフォルトの名無しさん
垢版 |
2024/03/15(金) 12:38:47.19ID:ohT/C40H
>>99
ってことは>>31の方法でも菱形継承問題ふつうに発生するやん

Iterable < Callable
__call__ = __next__

Associative < Callable
__call__ = __getattr__

Array < Iterable, Associative
__next__ = piyopiyo
__getattr__ = hogehoge

arr = Array(blahblahblah)
arr() はどっちになるべき?
0109デフォルトの名無しさん
垢版 |
2024/03/15(金) 14:11:38.88ID:DTF+UIWv
a) 実装継承
b) 多重継承
c) 多段階継承

ができる時点で菱形継承問題は避けられない
Rubyは、クラスは(b)を、モジュールは(c)を諦めることでこの問題を回避している
0110デフォルトの名無しさん
垢版 |
2024/03/15(金) 14:37:43.94ID:zsH6n39D
>>108
pythonはそうだよ
0111デフォルトの名無しさん
垢版 |
2024/03/15(金) 16:37:06.04ID:HEyD2qcN
Rubyのrefinementsみたいに、このスコープでは型定義を変えるみたいなのがあればまだマシになんのかな
たとえば、あるブロック内でだけ(Int, Int)をPoint2Dとみなすとか
0112デフォルトの名無しさん
垢版 |
2024/03/15(金) 17:16:49.88ID:Ratd2baE
>>108
・具象型とインタフェースを分ける
 具象型→Int, Str, List<T>など
 インタフェース→Comparable, Iterableなど
・多重継承したインタフェースで仮想でないメソッドに名前の重複があったらエラーにする

とするしかないのでは
0113デフォルトの名無しさん
垢版 |
2024/03/15(金) 17:20:27.60ID:Ratd2baE
型とインタフェースは分けないと、たとえば

equal(x: Eq, y: Eq) -> bool

とか無意味になる
xとyには具体的な型が同じものが入ってほしいのに、Eqインタフェースを持っている型すべてを許容してしまう
ほんとうにやりたいのは

equal(x: T, y: T) -> bool where T: Eq

ということ
0114デフォルトの名無しさん
垢版 |
2024/03/15(金) 17:33:55.76ID:UiCkupYq
型システムと並行性はむずかしいな
でも、言語がプログラマに好まれるかどうかってそんなとこにないと思うよ
一時期、高機能なタスクランナーやら単体テストフレームワークやらがたくさん出てきたけど、結局はnpm-scriptsとか言語標準のunittestライブラリに収束した
0115デフォルトの名無しさん
垢版 |
2024/03/15(金) 17:40:58.53ID:UiCkupYq
powershellはプログラミング言語としてはbashよりもはるかに高機能だが、誰もあれで書きたいとは思わない
0116デフォルトの名無しさん
垢版 |
2024/03/15(金) 18:11:48.66ID:RqiZ+uh9
>>108
inheritanceを型に対して行うのが間違いな気がする
Int x Intを、Point2DとRationalのサブクラスとしたとする
Point2DとRationalの両方に + を実装(前者にはベクトルとして、後者には有理数として)したとして、

(1, 2) + (3, 4)

はどうなるか?
それは結局、今(1, 2), (3, 4)をどの型だとみなしているかによるのであって、型Int x Intの与り知らぬところだ
0117デフォルトの名無しさん
垢版 |
2024/03/15(金) 18:25:53.61ID:C8O2+ggF
>>109
Mixinで多重継承問題回避できる理屈がよくわからんかったけど、今ようやっと理解できたわ
0120デフォルトの名無しさん
垢版 |
2024/03/15(金) 21:35:17.15ID:XzxHtHqU
ダイヤモンド継承は、コンパイラが検出するのが正しい気がする
構文で回避するには、>>109みたく機能を制限するしかない
RustとHaskellはしてくれる
0121デフォルトの名無しさん
垢版 |
2024/03/15(金) 21:53:50.95ID:WMhMrtUW
まあ、でもプログラミング言語が人気になるかどうかは
>>114の言うように、処理系の完璧さではなく、実用のニーズに耐え得ることなんだろうな
0122デフォルトの名無しさん
垢版 |
2024/03/15(金) 22:34:31.87ID:3o/QlmnN
クロージャとオブジェクトが同等なのはわかったが、それよりもさらに強力な言語機能はないのか?
0123デフォルトの名無しさん
垢版 |
2024/03/15(金) 23:30:35.88ID:rbMui8Ez
継続

プログラマが直接触らないものとしては、ガベージコレクションとかレキシカルクロージャとかもそうかもね
0124デフォルトの名無しさん
垢版 |
2024/03/16(土) 20:49:53.31ID:TBzj9DHS
Eiffel、Delphi、C#なんかは菱形継承してもそれぞれに別名を与えて捌ける機能を持ってるが
オブジェクト指向自体が下火なのもあって、最近のやつはそこまで作り込まれてない感
0125デフォルトの名無しさん
垢版 |
2024/03/16(土) 21:44:17.40ID:gqfrZq4+
多重継承は名前がぶつかろうがそれ自体に問題は一切ない
抽象型から具象型への継承で実装継承でないならば区別できる限り問題は生じない

本質的な問題は実装継承であること
特にに具象型から具象型への継承が問題
これは多重かどうかに関係なく問題となる
0126デフォルトの名無しさん
垢版 |
2024/03/16(土) 22:15:39.23ID:ajdJdfuW
そもそもOOPにおける継承ってのは、数学的に破綻してるんだよ
>>116で述べているとおり
型定義にサブタイピングの実装方法が入るわけがない
0127デフォルトの名無しさん
垢版 |
2024/03/16(土) 22:18:29.76ID:ajdJdfuW
自然数 is a モノイド

と自然にみなす方法は2通りある
その方法は自然数の定義とは関係ない
0129デフォルトの名無しさん
垢版 |
2024/03/16(土) 22:30:52.34ID:ajdJdfuW
行列式=1の2次正方行列が、
自分の型はM(2)なのか、GL(2)なのか、SL(2)なのかなんて知らんがな
0130デフォルトの名無しさん
垢版 |
2024/03/16(土) 22:35:57.52ID:TBzj9DHS
>>127の問題は型クラスやtraitでも発生するしなあ
C++で没になったconcept_mapのようなものを複数切り替えられる機能が求められる
0131デフォルトの名無しさん
垢版 |
2024/03/19(火) 03:55:14.66ID:5WuD+qyw
x: T where T: K where K: L where ...

のように高階の型や

fn(x: T) -> Type

のようにパラメータに依存する型などがいくらでも作れたらどんなことができるのだろうか
0132デフォルトの名無しさん
垢版 |
2024/03/19(火) 22:24:36.99ID:TBPz9mXO
>>131
具象型Tに対して抽象型Kの制約を課すところまでは普通として
抽象型Kに対して高階抽象型Lの制約を課すメリットが見つからなかった

代替の方法として高階とせずに
抽象型Kに対して同じレベルの抽象型Lの制約を常に課す宣言ができれば十分にみえる
0135デフォルトの名無しさん
垢版 |
2024/04/07(日) 01:46:27.75ID:HXZiHVf3
全称量化子は関数で
存在量化子はパターンマッチングで
表せるので、一階述語論理もラムダ式で書けそう
0136デフォルトの名無しさん
垢版 |
2024/04/07(日) 05:29:44.44ID:FOjhJ4gr
書けるの意味がわからんな
書いてどうする?
カリー・ハワード対応とかチューリング完全と関係ある話か?
0137デフォルトの名無しさん
垢版 |
2024/04/07(日) 06:21:18.75ID:ZeXbAXZK
存在命題とか背理法とか、仮定を仮引数にしたラムダ式を使えば、具体的に証明や値を構成しなくても所望のものを取ってこられるんだな
0139デフォルトの名無しさん
垢版 |
2024/04/07(日) 09:52:53.31ID:/E0pQilp
res = hoge()みたいにただ結果返すのと、
hoge((res) => doSomething())みたいに引数に結果入ってくるのと、何が違うんや
0140デフォルトの名無しさん
垢版 |
2024/04/07(日) 10:45:31.26ID:thR/d4RZ
まず、hogeが同期的でない場合は

res = hoge()

とは書けない
async/awaitのような機構が必要になる

またたとえば、エラーの場合は処理を分岐させたいような場合、

hoge(
(res) => doSomething(),
(err) => handleError())

のように拡張できる
ただし、内部でさらに同じようなことをやっているとコールバック地獄になる
0143デフォルトの名無しさん
垢版 |
2024/04/07(日) 19:27:40.79ID:m+fa22Uj
純関数型言語でなくても
モダンなプログラミング言語
Go、Rust、Zig、Nim、Julia、Elixirなどは
クラスおよびその継承を言語仕様から排除しておりクラスは存在しない

それら各々の言語は全く異なる方針を採っている言語だがクラス排除だけは全てが同じ方針である
クラスとその継承は悪手であるとプログラミング言語界では結論が出ている
0147デフォルトの名無しさん
垢版 |
2024/04/08(月) 14:29:36.32ID:LhsijIe9
P∧Qの導入則は、P -> Q -> P∧Q
これは(P, Q)ならばP∧Qとも読めるし
Pを仮定したとき、QならばP∧Qとも読める
0148デフォルトの名無しさん
垢版 |
2024/04/09(火) 18:44:27.40ID:kKsSVHOb
限定継続(reset/shift)の動きが意味わかんない
resetで範囲絞ってる分call/ccより実用的には扱いやすいというのは分かるが、ローカルで見たらcall/ccのほうが直感的に思える
0149デフォルトの名無しさん
垢版 |
2024/04/09(火) 23:18:54.13ID:Aro4tJCD
実装の都合では?
フル機能の継続はそこまでのスタック全部を生かし続けないといけないし分岐できないといけないしで
スタック自体をOSの用意してくれるものとは別に自前で構成しないといけないしそうするとABIからなにからつらい
0150デフォルトの名無しさん
垢版 |
2024/04/10(水) 01:07:10.63ID:uPucvtCR
内部で継続渡しに変換してるなら、フルの継続のほうが実装しやすいと思うけど
Schemeの場合、dynamic-windとかも実装しなきゃいかんからより複雑だろうけど
0151デフォルトの名無しさん
垢版 |
2024/04/10(水) 03:30:06.71ID:qIIVcoEj
継続渡しはスタック消費をクロージャで置き換えてるわけで、スタックを自前で用意してるのと同じようなもんでは
0154デフォルトの名無しさん
垢版 |
2024/04/10(水) 11:55:16.47ID:dXGnSmQj
処理が

A → B → C → ...

とあって

A → ... → shift(fn k -> hoge) → B → ... → C → reset

とすると

k に処理 _ → B → ... → Cが束縛される
hogeを実行するとresetまでジャンプする
resetの値はhogeの値になる

なので、たとえばバックトラックがしたいなら、
戻ってきたい場所にshiftを設置して、
fn () -> k next_valueをスタックに積んで、
クロージャの内部でk current_valueを実行すればいい
0155デフォルトの名無しさん
垢版 |
2024/04/10(水) 11:57:37.66ID:dXGnSmQj
で、スタックから継続をpopして実行する関数を別に作って
選択肢がなくなったときはそれを呼べばいい
レスを投稿する


ニューススポーツなんでも実況