• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-341  評論-2670  文章-0  trackbacks-0

            這個系列的起因是這樣的,王垠寫了一篇噴go的博客http://www.yinwang.org/blog-cn/2013/04/24/go-language/,里面說go已經爛到無可救藥了,已經懶得說了,所以讓大家去看http://www.mindomo.com/view.htm?m=8cc4f95228f942f8886106d876d1b041,里面有詳細的解釋。然后這篇東西被發上了微博,很多博友立刻展示了人性丑陋的一面:
            1、那些go的擁護者們,因為go被噴了,就覺得自己的人格受到了侮辱一樣,根本來不及看到最后一段的鏈接,就開始張牙舞爪。
            2、王垠這個人的確是跟人合不來,所以很多人就這樣斷定他的東西“毫無參考價值”。

            不過說實話,文章里面是噴得有點不禮貌,這也在一定程度上阻止了那些不學無術的人們繼續閱讀后面的精華部分。如果所有的文章都這樣那該多好啊,那么爛人永遠都是爛人,不糾正自己的心態永遠獲得不了任何有用的知識,永遠過那種月入一蛆的日子,用垃圾的語言痛苦的寫一輩子沒價值的程序。

            廢話就說到這里了,下面我來說說我自己對于語言的觀點。為什么要設計一門新語言?原因無非就兩個,要么舊的語言實在是讓人受不了,要么是針對領域設計的專用語言。后一種我就不講了,因為如果沒有具體的領域知識的話,這種東西永遠都做不好(譬如SQL永遠不可能出自一個數據庫很爛的人手里),基本上這不是什么語言設計的問題。所以這個系列只會針對前一種情況——也就是設計一門通用的語言。通用的語言其實也有自己的“領域”,只是太多了,所以被淡化了。縱觀歷史,你讓一個只做過少量的領域的人去設計一門語言,如果他沒有受過程序設計語言理論的系統教育,那只能做出屎。譬如說go就是其中一個——雖然他爹很牛逼,但反正不包含“設計語言”這個事情。

            因此,在21世紀你還要做一門語言,無非就是對所有的通用語言都不滿意,所以你想自己做一個。不滿意體現在什么方面?譬如說C#的原因可能就是他爹不夠帥啦,譬如說C++的原因可能就是自己智商太低hold不住啦,譬如說Haskell的原因可能就是用的人太少招不到人啦,譬如說C的原因可能就是實在是無法完成人和抽象所以沒有linus的水平的人都會把C語言寫成屎但是你又招不到linus啦,總之有各種各樣的原因。不過排除使用者的智商因素來講,其實有幾個語言我還是很欣賞的——C++、C#、Haskell、Rust和Ruby。如果要我給全世界的語言排名,前五名反正是這五個,雖然他們之間可能很難決出勝負。不過就算如此,其實這些語言也有一些讓我不爽的地方,讓我一直很想做一個新的語言(來給自己用(?)),證據就是——“看我的博客”。

            那么。一個好的語言的好,體現在什么方面呢?一直以來,人們都覺得,只有庫好用,語言才會好用。其實這完全是顛倒了因果關系,如果沒有好用的語法,怎么能寫出好用的庫呢?要找例子也很簡單,只要比較一下Java和C#就夠了。C#的庫之所以好用,跟他語言的表達能力強是分不開的,譬如說linq(,to xml,to sql,to parser,etc),譬如說WCF(僅考慮易用性部分),譬如說WPF。Java能寫得出來這些庫嗎?硬要寫還是可以寫的,但是你會發現你無論如何都沒辦法把他們做到用起來很順手的樣子,其實這都是因為Java的語法垃圾造成的。這個時候可以抬頭看一看我上面列出來的五種語言,他們的特點都是——因為語法的原因,庫用起來特別爽。

            當然,這并不要求所有的人都應該把語言學習到可以去寫庫。程序員的分布也是跟金字塔的結構一樣的,庫讓少數人去寫就好了,大多數人盡管用,也不用學那么多,除非你們想成為寫庫的那些。不過最近有一個很不好的風氣,就是有些人覺得一個語言難到自己無法【輕松】成為寫庫的人,就開始說他這里不好那里不好了,具體都是誰我就不點名了,大家都知道,呵呵呵。

            好的語言,除了庫寫起來又容易又好用以外,還有兩個重要的特點:容易學,容易分析。關于容易學這一點,其實不是說,你隨便看一看就能學會,而是說,只要你掌握了門道,很多未知的特性你都可以猜中。這就有一個語法的一致性問題在里面了。語法的一致性問題,是一個很容易讓人忽略的問題,因為所有因為語法的一致性不好而引發的錯誤,原因都特別的隱晦,很難一眼看出來。這里我為了讓大家可以建立起這個概念,我來舉幾個例子。

            第一個例子是我們喜聞樂見的C語言的指針變量定義啦:

            int a, *b, **c;

            相信很多人都被這種東西坑過,所以很多教科書都告訴我們,當定義一個變量的時候,類型最后的那些星號都要寫在變量前面,避免讓人誤解。所以很多人都會想,為什么要設計成這樣呢,這明顯就是挖個坑讓人往下跳嘛。但是在實際上,這是一個語法的一致性好的例子,至于為什么他是個坑,問題在別的地方。

            我們都知道,當一個變量b是一個指向int的指針的時候,*b的結果就是一個int。定義一個變量int a;也等于在說“定義a是一個int”。那我們來看上面那個變量聲明:int *b;。這究竟是在說什么呢?其實真正的意思是“定義*b是一個int”。這種“定義和使用相一致”的方法其實正是我們要推崇的。C語言的函數定義參數用逗號分隔,調用的時候也用逗號分隔,這是好的。Pascal語言的函數定義參數用分號分隔,調用的時候用逗號分隔,這個一致性就少了一點。

            看到這里你可能會說,你怎么知道C語言他爹就是這么想的呢?我自己覺得如果他不是這么想的估計也不會差到哪里去,因為還有下面一個例子:

            int F(int a, int b);
            int (*f)(int a, int b);

            這也是一個“定義和使用相一致”的例子。就第一行代碼來說,我們要如何看待“int F(int a, int b);”這個寫法呢?其實跟上面一樣,他說的是“定義F(a, b)的結果為int”。至于a和b是什么,他也告訴你:定義a為int,b也為int。所以等價的,下面這一行也是“定義(*f)(a, b)的結果為int”。函數類型其實也是可以不寫參數名的,不過我們還是鼓勵把參數名寫進去,這樣Visual Studio的intellisense會讓你在敲“(”的時候把參數名給你列出來,你看到了提示,有時候就不需要回去翻源代碼了。

            關于C語言的“定義和使用相一致”還有最后一個例子,這個例子也是很美妙的:

            int a;
            typedef int a;
            
            int (*f)(int a, int b);
            typedef int (*f)(int a, int b);

            typedef是這樣的一個關鍵字:他把一個符號從變量給修改成了類型。所以每當你需要給一個類型名一個名字的時候,就先想一想,怎么定義一個這個類型的變量,寫出來之后往前面加個typedef,事情就完成了。

            不過說實話,就一致性來講,C語言也就到此為止了。至于說為什么,因為上面這幾條看起來很美好的“定義和使用相一致”的規則是不能組合的,譬如說看下面這一行代碼:

            typedef int(__stdcall*f[10])(int(*a)(int, int));
            這究竟是個什么東西呢,誰看得清楚呀!而且這也沒辦法用上面的方法來解釋了。究其原因,就是C語言采用的這種“定義和使用相一致”的手法剛好是一種解方程的手法。譬如說int *b;定義了“*b是int”,那b是什么呢,我們看到了之后,都得想一想。人類的直覺是有話直說開門見山,所以如果我們知道int*是int的指針,那么int* b也就很清楚了——“b是int的指針”。 

            因為C語言的這種做法違反了人類的直覺,所以這條本來很好的原則,采用了錯誤的方法來實現,結果就導致了“坑”的出現。因為大家都習慣“int* a;”,然后C語言告訴大家其實正確的做法是“int *a;”,那么當你接連的出現兩三個變量的時候,問題就來了,你就掉坑里去了。

            這個時候我們再回頭看一看上面那一段長長的函數指針數組變量的聲明,會發現其實在這種時候,C語言還是希望你把它看成“int* b;”的這種形式的:f是一個數組,數組返回了一個函數指針,函數返回int,函數的參數是int(*a)(int, int)所以他還是一個函數指針。

            我們為什么會覺得C語言在這一個知識點上特別的難學,就是因為他同時混用了兩種原則來設計語法。那你說好的設計是什么呢?讓我們來看看一些其它的語言的作法:

            C++:
            function<int __stdcall(function<int(int, int)>)> f[10];
            
            C#:
            Func<Func<int, int, int>, int>[] f;
            
            Haskell:
            f :: [(int->int->int)->int]
            
            Pascal:
            var f : array[0..9] of function(a : function(x : integer; y : integer):integer):integer;

             

            這些語言的做法,雖然并沒有遵守“定義和使用相一致”的原則,但是他們比C語言好的地方在于,他們只采用一種原則——這就比好的和壞的混在一起要強多了(這一點go也是,做得比C語言更糟糕)。

            當然,上面這個說法對Haskell來說其實并不公平。Haskell是一種帶有完全類型推導的語言,他不認為類型聲明是聲明的一部分,他把類型聲明當成是“提示”的一部分。所以實際上當你真的需要一個這種復雜結構的函數的時候,實際上你并不會真的去把它的類型寫出來,而是通過寫一個正確的函數體,然后讓Haskell編譯器幫你推導出正確的類型。我來舉個例子:

            superApply fs x = (foldr id (.) fs) x

             

            關于foldr有一個很好的理解方法,譬如說foldr 0 (+) [1,2,3,4]說的就是1 + (2 + (3 + (4 + 0)))。而(.)其實是一個把兩個函數合并成一個的函數:f (.) g = \x->f(g( x ))。所以上述代碼的意思就是,如果我有下面的三個函數:

            add1 x = x + 1
            mul2 x = x * 2
            sqr x = x * x

             

            那么當我寫下下面的代碼的時候:

            superApply [sqr, mul2, add1] 1
            的時候,他做的其實是sqr(mul2(add1(1)) = ((1+1)*2) * ((1+1)*2) = 16。當然,Haskell還可以寫得更直白:
            superApply [(\x->x*x), (*2), (+1)] 1

             

            Haskell代碼的簡潔程度真是喪心病狂啊,因為如果我們要用C++來寫出對應的東西的話(C語言的參數無法是一個帶長度的數組類型所以其實是寫不出等價的東西的),會變成下面這個樣子:

            template<typename T>
            T SuperApply(const vector<function<T(T)>>& fs, const T& x)
            {
                T result = x;
                for(int i=fs.size()-1; i>=0; i--)
                {
                    result = fs[i](result);
                }
                return result;
            }

             

            C++不僅要把每一個步驟寫得很清楚,而且還要把類型描述出來,整個代碼就變得特別的混亂。除此之外,C++還沒辦法跟Haskell一樣吧三個函數直接搞成一個vector然后送進這個SuperApply里面直接調用。當然有人會說,這還不是因為Haskell里面有foldr嘛。那讓我們來看看同樣有foldr(reverse + aggregate = foldr)的C#會怎么寫:

            T SuperApply<T>(Func<T, T>[] fs, T x)
            {
                return (fs
                    .Reverse()
                    .Aggregate(x=>x, (a, b)=>y=>b(a(y)))
                    )(x);
            }

             

            C#基本上已經達到跟Haskell一樣的描述過程了,而且也可以寫出下面的代碼了,就是無論聲明和使用的語法的噪音稍微有點大……

            SuperApply(new Func<T, T>[]{
                x=>x*x,
                x=>x*2,
                x=>x+1
                }, 1);

             

            為什么要在討論語法的一致性的時候說這些問題呢,在這里我想向大家展示Haskell的另一種“定義和使用相一致”的做法。Haskell整個語言都要用pattern matching去理解,所以上面的這段代碼

            superApply fs x = (foldr id (.) fs) x
            說的是,凡是你出現類似superApply a b的這種“pattern”,你都可以把它當成(foldr id (.) a) b來看。譬如說
            superApply [(\x->x*x), (*2), (+1)] 1
            其實就是
            (foldr id (.) [(\x->x*x), (*2), (+1)]) 1
            只要superApply指的是這個函數,那無論在什么上下文里面,你都可以放心的做這種替換而程序的意思絕對不會有變化——這就是haskell的帶有一致性的原則。那讓我們來看看Haskell是如何執行他這個一致性的。在這里我們需要知道一個東西,就是如果我們有一個操作符+,那我們要把+當成函數來看,我們就要寫(+)。如果我們有一個函數f,如果我們要把它當成操作符來看,那就要寫成`f`(這是按鍵!左邊的那個符號)。因此Haskell其實允許我們做下面的聲明:
            (Point x y) + (Point z w) = Point (x+z) (y+w)
            (+) (Point x y) (Point z w) = Point (x+z) (y+w)
            
            (Point x y) `Add` (Point z w) = Point (x+z) (y+w)
            Add (Point x y) (Point z w) = Point (x+z) (y+w)

             

            斐波那契數列的簡單形式甚至還可以這么寫:

            f 1 = 1
            f 2 = 1
            f (n+2) = f(n+1) + f(n)

             

            甚至連遞歸都可以寫成:

            GetListLength [] = 0
            GetListLength (x:xs) = 1 + GetListLength xs

             

            Haskell到處都貫徹了“函數和操作符的替換關系”和“pattern matching”兩個原則來做“定義和實現相一致”的基礎,從而實現了一個比C語言那個做了一半的混亂的原則要好得多的原則。

            有些人可能會說,Haskell寫遞歸這么容易,那會不會因為鼓勵人們寫遞歸,而整個程序充滿了遞歸,很容易stack overflow或者降低運行效率呢?在這里你可以往上翻,在這篇文章的前面有一句話“好的語言,除了庫寫起來又容易又好用以外,還有兩個重要的特點:容易學,容易分析。”,這在Haskell里面體現得淋漓盡致。

            我們知道循環就是尾遞歸,所以如果我們把代碼寫成尾遞歸,那Haskell的編譯器就會識別出來,從而在生成x86代碼的時候把它處理成循環。一個尾遞歸遞歸函數的退出點,要么是一個不包含自身函數調用的表達式,要么就是用自身函數來和其它參數來調用。聽起來比較拗口,不過說白了其實就是:

            GetListLength_ [] c = c
            GetListLength_ (x:xs) c = GetListLength_ xs (c+1)
            GetListLength xs = GetListLength_ xs 0

             

            當你寫出這樣的代碼的時候,Haskell把你的代碼編譯了之后,就會真的輸出一個循環,從而上面的擔心都一掃而空。

            實際上,有很多性能測試都表明,在大多數平臺上,Haskell的速度也不會被C/C++慢超過一倍的同時,要遠比go的性能高出許多。在Windows上,函數式語言最快的是F#。Linux上則是Scala。Haskell一直都是第二名,但是只比第一名慢一點點。

            為了不讓文章太長,好分成若干次發布,每次間隔都較短,所以今天的坑我只想多講一個——C++的指針的坑。剩下的坑留到下一篇文章里面。下面要講的這個坑,如果不是在粉絲群里面被問了,我還不知道有人會這么做:

            class Base
            {
              ...
            };
            
            class Derived : public Base
            {
              ...
            };
            
            Base* bs = new Derived[10];
            delete[] bs;

             

            我想說,這完全是C++兼容C語言,然后讓C語言給坑了。其實這個問題在C語言里面是不會出現的,因為C語言的指針其實說白了只有一種:char*。很多C語言的函數都接受char*,void*還是后來才有的。C語言操作指針用的malloc和free,其實也是把他當char*在看。所以當你malloc了一個東西,然后cast成你需要的類型,最后free掉,這一步cast存在不存在對于free能否正確執行來說是沒有區別的。

            但是事情到了C++就不一樣了。C++有繼承,有了繼承就有指針的隱式類型轉換。于是看上面的代碼,我們new[]了一個指針是Derived*類型的,然后隱式轉換到了Base*。最后我們拿他delete[],因為delete[]需要調用析構函數,但是Base*類型的指針式不能正確計算出Derived數組的10個析構函數需要的this指針的位置的,所以在這個時候,代碼就完蛋了(如果沒完蛋,那只是巧合)。

            為了兼容C語言,“new[]的指針需要delete[]”和“子類指針可以轉父類指針”的兩條規則成功的沖突到了一起。實際上,如果需要解決這種問題,那類型應該怎么改呢?其實我們可以跟C#一樣引入Derived[]的這種指針類型。這還是new[]出來的東西,C++里面也可以要求delete[],但是區別是他再也不能轉成Base[]了。只可惜,T[]這種類型被C語言占用了,在函數參數類型里面當T*用。C語言浪費語法罪該萬死呀……

            待續

            posted on 2013-04-27 01:24 陳梓瀚(vczh) 閱讀(33217) 評論(37)  編輯 收藏 引用 所屬分類: 啟示

            評論:
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 02:02 | 溪流
            學習了  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 02:20 | sev
            板凳了..  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 03:43 | skyshaw
            “GetListLength_ [] c = x” 應該是c吧  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 04:09 | 陳梓瀚(vczh)
            @skyshaw
            改了  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 06:22 | 蔡東赟
            mark,先下班回家看  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 06:25 | zhenqu
            不明覺厲。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 07:48 | Richard Wei
            mark, 拜膜...  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-04-27 16:14 | 123
            小陳水平又提高了 呵呵  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 18:39 | tinro
            int &b; &b是int?當然,你也可以說C++不是C。int *b,和*b里面的*,不好簡單作為一個含義來解釋。

            至于Haskell,從數學角度看很美好,從工程角度看很糟糕。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 20:32 | clang
            看不懂他想噴什么,怎么辦?  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-04-27 20:37 | 不懂
            雖然很認真的看了一遍,還是沒看懂“如何設計一門語言”的意思,只能說樓主智商太高了  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-04-29 04:37 | raof01
            typedef int(__stdcall*f[10])(int(*a)(int, int));
            這個不是:“f是一個數組,數組返回了一個函數指針,……”。數組不會返回什么東西的啊。我覺得用typedef來解釋更清楚。上面的typedef等價于:
            typedef int (*A)(int, int);
            typedef int (__stdcall*F)(A);
            F[10];  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-03 23:21 | Quon
            想改變現狀的人做了個新的語言出來,于是想出名的人有了可以噴的東西,評論家靠發明家吃飯.  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-05-15 22:13 | peakflys
            對于博主最后一個例子所說的“但是Base*類型的指針式不能正確計算出Derived數組的10個析構函數需要的this指針的位置的”,請問為什么不行呢?難點在哪?  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-05-15 22:17 | peakflys
            剛才構造了一個例子,如下:
            #include <cstdio>

            class Base
            {
            public:
            Base(const int a=10) : a(a)
            {
            printf("Base constructor,this addr: %p\n",this);
            }
            virtual ~Base()
            {
            printf("Base destructor, a=%d, this addr: %p\n",a,this);
            }
            int a;
            };
            class Derive : public Base
            {
            public:
            Derive(const int a=20,const int b=20) : Base(a),b(b)
            {
            printf("Derive constructor,this addr: %p\n",this);
            }
            virtual ~Derive()
            {
            printf("Derive destructor, b=%d, this addr: %p\n",b,this);
            }
            int b;
            };
            int main(int argc, char* argv[])
            {
            Base *pb = new Derive[3];
            delete [] pb;
            printf("over\n");
            return 0;
            }
            特意在基類和派生類里各加了一個成員變量,來增大兩者可能的指針偏移。
            結果證明 delete 多態的指針后,內存正常釋放,而每個派生類對象的this指針也均無差錯!
            編譯器:g++ (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-16 00:04 | peakflys
            最后一個例子不是什么坑,只不過是ISO未定義的情況,其實要實現基類指針正確找到派生類this偏移的方式很多種,記下大小,加上固定偏移就是一種方法,當然還有其他很多種方法,應該沒什么特別的復雜,這里頂多告誡大家要慎用,因為是標準未定義的行為,crash or not crash,compiler decides it。建議博主修改之。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-16 17:17 | weslee
            那么爛人永遠都是爛人,不糾正自己的心態永遠獲得不了任何有用的知識,永遠過那種月入一蛆的日子,用垃圾的語言痛苦的寫一輩子沒價值的程序。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-25 22:56 | Marvin
            我對一門好的語言的定義是
            你對語言的定位是什么,它恰當的解決了你定位的問題。

            個人覺得,如果按我的這種定義,只有2個語言很好。一個是C,一個是Ruby.

            如果你不懂,我給你舉個例子。C++號稱要代替C成為主流開發語言。它是要在C的基礎上加入面向對象。可結果是1.在關鍵的時候,還點用C而不是用C++,比如寫操作系統。2. C++號稱是要在C的基礎上加入面向對象,這個目標完成的怎么樣我不做評論,但它只加入了面向對象嗎?那個++到底要加什么,恐怕連C++的設計者也不清楚。
            再一個例子,Java看到C++有問題,號稱叫C++--,它減掉了C++不好的東西了嗎?

            按我的理想,真正好語言還沒有出現。不然就不會出來這個多號稱要改變這個,改變那個的語言了。

            另外,我最常用的語言是C++, JavaScript. 也用過C#, Java, Obj-C, Ruby, PHP, Go等  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-25 22:57 | Marvin
            Sorry,上面的主頁地址寫錯了  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-05-28 19:19 | 陳梓瀚(vczh)
            @Marvin
            問題解決的再好,語言設計的爛也沒用,看yacc,看antlr,只會讓你學起來痛苦。一門好的語言,首當其沖就是“到處都沒有特例”,其次才考慮怎么良好的解決某些問題——不過像C++這種語言其實可以解決大量的問題的同時也解決的好,缺點是你要經過大量的訓練,很少人可以做到。學起來太難,好處就被抵消了。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-06-03 21:09 | Marvin
            @陳梓瀚(vczh)
            C++有難度,我承認,但好像你沒有看明白我上面寫的內容。關鍵問題是C++增加的這些難度是否是值得的,能有效解決本質問題的方法是最好的。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-06-04 19:44 | 陳梓瀚(vczh)
            @Marvin
            這些難度100%是因為兼容C語言+RAII而必須產生的。兼容C是值得的,RAII也是值得的,所以都是值得的。你無論去掉哪個,要么就不兼容C,要么RAII就不完整。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-07-13 05:51 | 幻の上帝
            @陳梓瀚(vczh)
            需要RAII是靜態語言+靜態類型系統的原罪。
            WG21本可以不照抄ISO/IEC 9899的一堆translation phases,允許C++提供動態語言的接口。結果還是抄了。為什么?怕無所適從?照顧習慣?市場原因?
            的確C的渣是C++設計最重大的包袱。但要說100%就過了。舉幾個C++原創的渣設計:
            1.new-expression的語法。
            2.復雜value category的必要性。
            3.混合不同的類型系統導致無法交互。這樣說可能太抽象,一個顯眼的問題是模板為什么要獨立于類型系統之外?操作符sizeof...為什么不能由庫實現?
            當然關于類型系統C有更多負資產。不過至少就這幾個例子不可能是C的過錯。
              回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-07-13 09:53 | 陳梓瀚(vczh)
            @幻の上帝
            RAII的原罪顯然是內存太貴,這個根本沒有別的原因,你怎么不說GC是原罪呢。真實的

            我就不說你那個亂七八糟的評論了,至少
            1:template怎么就在類型系統以外了
            2:sizeof要用庫實現也不是不可能,至少在我現在在設計的語言里面就是可以的,不過這個東西是C的事情,C可沒有什么“用于偏特化的遞歸模板類型”這一功能的,你讓他怎么做?  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a)[未登錄] 2013-07-13 09:55 | 陳梓瀚(vczh)
            @幻の上帝
            我怕你看不明白再說一句,為什么python打開文件后也要close,還不是因為沒有RAII,你又不能等gc來日,只好自己動手了嗎。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-10-20 00:38 | 幻の上帝
            @陳梓瀚(vczh)
            GC當然也是原罪。是不是必要呢,這是另外一回事了:不過我確信在靜態語言里強迫用戶使用GC更愚蠢,RAII在這里當然是更好的解決方案。所以關于RAII的必要性你沒必要對我科普。
            根源在于向機器明確資源管理的邏輯是程序員的義務——現在顯然沒有AI能從根本上精確猜測用戶的意圖以自動分清楚哪些資源是適合確定性析構的,而哪些更適合延緩回收作為優化。
            1.無論如何,C++的template不是type。無論從概念定義還是實用上來講template都和C++的type有顯著區別。盡管有些東西有共性,但在語言規則上做不到。就算不管這個,還得考慮一些上下文:如C++11對T&&的類型推導規則就因為T是或者不是類型參數而有所不同。
            2.省略號沒被吃吧。是sizeof...不是sizeof。你試試看,打算重載幾個?別忘了模板非類型參數和模板模板參數。


              回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-10-20 00:41 | 幻の上帝
            還有,這里我當然沒有在說C,C在這上面的無能顯而易見。
            我是在列舉C++原創的沒有照搬C的比較欠抽的設計。一些問題不能在C++里解決說明C++在這里的失敗——這也當然不因為你能發明新語言而當作解決了因此可以無視掉。

              回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-10-26 16:54 | Mr.Okay
            樓主您好!我是個語言設計方面的愛好者和學習者。您的文章真是寫到我心里去了——我不是專業的,所以不可能像您這樣表達的這么清楚。對于C語言在語法方面的不一致,我一直想知道,下面這個例子是不是一個體現:
            int a, *b;
            int* F(int a, int b);
            第一行里,它希望你把*b看為整數,而第二行又希望你把int*當作一種類型。  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-10-27 22:04 | 陳梓瀚(vczh)
            @Mr.Okay
            所以說這就是坑啊  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2013-11-01 10:18 | 幻の上帝
            不吱聲么。
            覺得sizeof...無厘頭的話就換一個比較能造成實際困難的例子。
            self-application lambda abstraction (λu.u u)在C艸里要寫成什么類型的?如何用一個name表示這個在類型論意義上完全可以就是type但C艸就是不認(也構造不出)的東西?
            簡而言之,除了在遞歸類型上的不必要限制(函數不能聲明自身類型為參數)外,C艸聲稱:
            全稱類型(這里的模板)不是C++類型。C++類型不被量化。
            愚蠢的設計。
              回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:29 | xnature
            看到一半看不懂了

            “就是因為他同時混用了兩種原則來設計語法”

            哪兩種?前文似乎只說了一種啊  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:38 | xnature
            前文開始C++定義中的function是什么東西啊?  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-05-11 12:54 | xnature
            最后一個例子在C++中正確的做法應該是怎樣的?  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-05-12 07:39 | 陳梓瀚(vczh)
            @xnature
            std::function是C++11的一個類  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-05-12 07:39 | 陳梓瀚(vczh)
            @xnature
            最后一個例子的正確做法當然是Derived* ds = new Derived[10];  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-07-03 07:56 | dramforever
            沒人注意haskell? 樓主一定沒測試GetListLength
            函數第一個字母小寫哦  回復  更多評論
              
            # re: 如何設計一門語言(一)&mdash;&mdash;什么是坑(a) 2014-10-26 19:00 | 幻の上帝
            借個地方。
            關于sizeof...那個嘛。。Google Groups上不去(本來應該回復在跟Nicol Bolas的討論的)……
            http://en.wikipedia.org/wiki/Pure_type_system
            看來早幾年我又在發明無聊輪子了。
            (BTW,f*ck WG21/N1919。)
              回復  更多評論
              
            久久人人爽人人爽人人片AV麻豆 | 久久WWW免费人成—看片| 国产一级做a爰片久久毛片| 久久综合亚洲欧美成人| 99久久精品午夜一区二区| 青青草原综合久久| 四虎影视久久久免费观看| 亚洲成色www久久网站夜月| 久久国产亚洲精品麻豆| 思思久久好好热精品国产| 久久天天躁狠狠躁夜夜网站| 九九久久精品国产| 欧美熟妇另类久久久久久不卡| 国产69精品久久久久99| 久久国产亚洲精品| 伊人色综合久久天天| 欧美粉嫩小泬久久久久久久| 99久久人妻无码精品系列蜜桃| 少妇久久久久久被弄到高潮| 狠狠色丁香婷婷综合久久来| 久久久午夜精品| 久久精品国产亚洲AV不卡| 国内精品久久久久影院优| 久久久久久午夜精品| 激情五月综合综合久久69| 久久ww精品w免费人成| 狠狠综合久久AV一区二区三区| 久久se精品一区二区影院| 久久er国产精品免费观看2| 午夜精品久久久久久久| 精品熟女少妇AV免费久久| 国产精品亚洲美女久久久| 亚洲国产二区三区久久| 2021久久国自产拍精品| 97久久天天综合色天天综合色hd| 久久天天婷婷五月俺也去| 伊人久久大香线蕉综合网站| 日本加勒比久久精品| 人妻精品久久久久中文字幕| 久久久久亚洲国产| 亚洲中文字幕无码久久精品1 |