OOC, generics, bad design.

Source: Internet
Author: User

Although most of the talk about OOC compiler design, but more content lies in the idea of programming, complexity, maintenance above. I hope this article will help the reader even a little.

This article follows CC-BY-NC.

= OOC, generics, with those bad designs original address: Http://fasterthanlime.com/blog/2015/ooc-generics-and-flawed-designs This article is also posted on GitHub: HTTPS ://github.com/zhaihj/ch_cn-ooc-generics-and-flawed-designed translation: [email protected]/[email protected] Time: 2015/01/20

OOC may be the proudest accomplishment of my (note: the original author), but at the same time, it is also a "thorn head" that I have a headache for me.

An important reason is that its design (architecture) is not satisfying, and in the present, a lot of things can not be easily solved. But don't misunderstand: in a sense, all designs are "bad", either by a single person or by a community-driven one. No one design can be called perfect. Although we do not have a definition and measure of "perfect".

Now back to OOC, some topics have been repeatedly mentioned, such as Interface (interface) and cover (overlay). Part of the reason for this problem is that people would simply think that these concepts should be the same as in other languages because their names are identical. But this is not the case, even if they have the same name, they have different meanings. If these concepts are identical to the concepts in other languages, then OOC will be a simple c++/java/ruby implementation of the underlying. But OOC not, OOC and each of the above languages have coincident, and I (note: the original author) and the past six years to help me together people, then try to combine these characteristics together.

= = About Generics

This is the definition of generics in OOC: It is a pointer to an unknown type and holds the corresponding value (with value semantics).

First let's look at a simple generic function (generic functions):

Add:func <T> (A, b:t), t{    A + b}

Translator Note: For people unfamiliar with OOC, explain the above code: The above code defines a function called Add, the parameter is a, b (generic), the return value is the sum of the two.

Now, if OOC's generic (generic) is a template, there is no problem with this code-because it generates an "add" implementation based on the type. If a b the type is different, throw an error, if T does not have a "+" operator overload, throws an error, and so on.

But that's not how the OOC works. For a OOC generic, at run time, you know it is only:

    • Which part of the memory it is in (pointers to it)
    • What type it is (name + size + pointer size)

But at compile time, you don't get anything. Yes, we don't get anything. At compile time, what you can do to generics is "copy (move) it to different places". This is enough for collections (collection), because in the vast majority of cases, and so on, ArrayList<T> HashMap<K,V> you don't need to know much about the type of elements inside.

Well, I lied, and they still need to know something. But most of the cases are for comparisons and hashes. For example, ArrayList<T> indexOf(value: T) -> Int we need to compare the values of two t types, otherwise we cannot search for it from within the array.

For HashMap<K,V> , we need to hash the "K", otherwise we can't save the value, and we can't get the hash value of the key when we search. Also, to avoid collisions, we need to compare the values of K.

In OOC's standard library (note: In OOC, the standard library is called the SDK), how do we do it? We used a couple of little tricks (cheating). You should remember what I said, at run time, we only know the address and type of generics. OK, we can write this:

Add:func <T> (A, b:t), T {match T {case Int =>a as Int + b as Intcase = = Exception New ("Don ' t know how To add type #{t name} ") throw ()}}

Now, a lot of people will start to think this is a very bad design, I (note: the original author) should not even call this "generics", and so on. But that's how it works. Even if I complained to 6 years ago why I chose such a design, it doesn't make any sense to me. I'm just trying to explain how it works, to eliminate possible misunderstandings in the future.

OK, let's go back to HashMap (note: An implementation in the standard library), when you build a hashmap, it will be similar to the above, according to the type of K choose the best hash function, this depends on K is not a string, is not a number, or something else.

= = Type value (type as values)

In a question report from GitHub (note: That's what I'm reporting ...). ), some say that for the following function

Identity:func <T> (t:t) t {t}

This code can be run:

Identity (42)

But this cannot:

Identity<int> (42)

But if you define a paradigm class:

Cell:class <t>{    t:t    Init:func}

Then you can use any of the following:

Cell: = cell<int> new ()

Usually, this is a bad one, but at the same time how much maintains the internal consistency of the design. There is a reason for this kind of thing.

For the cell (generic class), the constructor (constructor) does not accept any instances of T--that is, in the constructor, we cannot infer the type of T, so we need to specify a type in angle brackets. Generics can be used as type parameters-that's how it works.

But the function is different. In the same case, if the inferred type is not the type you want (in the example below is Ssizet), then you can always cast the call function to what you want:

Identity ($)//-ssizetidentity (as int)//INT

There are times when it is possible to infer types based on parameters at compile time, such as:

Getsome:func <T> t{    match T {case        Int =             4//Guaranteed by Fair dice Roll Case        Float =&G t;            0.8 Case        =             raise ("Can ' t get some of #{t name}"}    }

This function can never be called normally. In other languages, you can simply use getSome<Int>() or specify the getSome<Float>() type, but in Ooc, you can't. Instead, you need to put the type T in the argument list, for example:

Getsome:func <T> (t:class), t{    match t{        //etc ...    }}

Now when you can getSome(Int) getSome(Float) call this function through or. Come back again, on this issue, we can always endlessly argue that one is the better way. For others, "This is OK: pass the type with angle brackets, pass the argument with the square brackets", and six years ago, I had a unique belief in the tag: the type is just a value, and as with the other values, we don't have to add a variety of restrictions around them (note: The meaning of parentheses).

Six years ago I think so: if we have a "partial" primitive, then we can simply getSome(Int) convert to getSomeInt , and then use the new function elsewhere, and think it returns an int type:

Fictional code (6-years-ago-me way)//pseudocode (My Practice 6 years ago) Getsome:func <T> (t:class), T {/* see above */}getsom Eint:partial (Getsome, Int) eng: = GameEngine new () Eng Setrandomnumbergenerator (Getsomeint)

In turn, what can you do under the style of the angle brackets? Well, it doesn't make much difference to define a new function with yourself, or use closures:

Fictional code (6-years-ago-me way)//pseudocode (My Practice 6 years ago) Getsome:func <T> (t:class), T {/* see above */}eng: = GameEngine new () Eng Setrandomnumbergenerator (| | getsome<int> ())

This way you lose some of the higher-order (High-order) function tools. Again, this is not a big problem in the current OOC, because there is no such thing as a partial in the standard library.

= = Return to value

As I mentioned earlier, it is difficult to do some useful action relative to the model value. Unless you cast them back to the "real" type:

Somefunction:func<t> (t:t) {    //' t ' is kind of useless    //' t ' does not have any use    u: = T    as Int//Now, with ' u ' We can do anything we want.    Now, we can do what we want to do with ' u '.

Converting a generic to a Narimi type is a dangerous operation. Because once the type is wrong, we can only get some messy garbled--the compiler doesn't do any checking, because it believes you know what you're doing (and, of course, it could be a bad operation). But six years ago I retorted that there was a pretty good mechanism that would make it quite safe for us to put generic conversions into real-type:

Somefunction:func <T> (t:t) {    match T {case        u:int =            //we ' re sure ' T ' is an int. and now we can Use it as U            //We determine that ' t ' is of type int and therefore can be used with u case        =            //error handling goes here.            This will throw an error        }}

These are everything that generics work on. On top of the above design, the (type) inference device should be quite clever (just imagine ...). So you can omit as many types of judgments as possible.

For example, the following code can be executed normally in the system described above:

Sorter:class <T> {    compare:func (T, T), Int    Init:func (=compare) {}    Sort:func (l:list<t> )-list<t> {/* ... */}}s: = Sorter<int> (|a, b| (A < b)? -1: (a > B. 1:0)) s sort ([1, 2, 3] as arraylist<int>)

= = "Maintenance" is a difficult job

Now, whenever someone misconceptions about generics, overlays, interfaces, and emphasizes how they should work, because "there's the same name in language X or Y", I don't know how to deal with it.

Sometimes, they are normal bugs and we can fix it. In this case, I'd be happy to use my spare time to help out. But sometimes, it's impossible. This is often a two-choice question:

    • In the case of 1%, "luck" gives 12 things people should know (until the next person steps on another mine and then complains about the problem, which looks fair!) )。 Keep hitting new patches on the already patched compilers.
    • Select the main road. To discuss how this feature should work, and then rewrite the compiler code for 10%, 20%, or 50% (6 years ago I didn't know how to write a real modular compiler, but still stuck a bunch of stuff in it-it looked great, it's really troublesome now), and then 80% Existing code cannot compile (not many, but there are still hundreds of lines, and people use it every day, including me)

The first idea, it is often used. But it does not solve the problem. When you have a good, solid design, patches can make the things you want better. But when your design sucks, just like I did 6 years ago, adding patches is just bucket and technically untenable, and more users will be disappointed on this path.

It's like in your game that you have a problem: "The player can't climb the uneven face." Then someone will stand up and say, "Look, in the third level (WORLD3), there is a ladder in the hill behind the lake, but it seems to be unable to crawl, this is the patch." Then this patch is as long as it comes from the IP "174." Player, standing under the ladder, and then holding the ctrl+shift+alt up and looking up, will be transferred to the top immediately. Of course, "You can't get down the ladder, this will be solved in the next version!" ”

Imagine if there is no next version, because developers notice that the problem is far more complex than their original imagination (the ladder that climbs the hill behind the lake). Only smooth planes can work. Because they do not have time to rewrite the entire game engine, so that the non-smooth plane can also be considered within the return. So they started watching the next game. Even if this patch is adopted, it is only rotten there-because no one would have thought of Ctrl+shift+alt to see a particular position at the same time.

For you, the game developer, you should put the "game only supported in the smooth plane" into the document. But you may not want to do this because you may be ashamed to record it because it symbolizes that you have no time and energy (others will think of it as technology) to fix such a problem. In short, as an expert, you should do better.

So you didn't put the question on the game's homepage, because that was a huge negative effect on the publicity. It's a good choice, it balances lies and hurts for yourself. And, you start another game project, but you'll still occasionally go back to that game, play it, and add something. So it's not completely abandoned, but it's no longer your focus. It's great for long-term gamers because they know these things and they still get a lot of fun out of the smooth plane.

= = Conclusion

I should have received the pen before the metaphor became lengthy. But I hope you can understand what I say. When you're dealing with a poorly designed system (a concept that's everything in programming), sometimes it's worth trying and changing something to make it better. But most of the time, the best way to do this is to try to achieve the goal you want, under the constraints of this poorly designed system. If not, the easiest way to do that is to switch to another (poorly designed) system (and then you'll regret it, because the system doesn't have the features you wanted in the previous system ... and then repeats).

The following have been lingering in my sleep: how can I make it "perfect"? Does it really fit everyone's usage? Isn't the code pretty? What about speed? Can a wasted young man continue to write code before the end of his life?

But not now (at least not like before), because now I'm satisfied with the combination of these poorly designed systems and then do what I want to do. I will also leave some beautiful fragments to make my enjoyment more artistic. Sometimes it's a snowman (one of the Unicode Snowman,unicode characters) and sometimes it's a strange hint. It's naïve, this article looks like it's avoiding responsibility, I still sigh when I see someone hoping to change the majority of the OOC, so that he gets better, but I don't think it will happen. That's the role of C + + or Scala, and I don't want to take that responsibility.

OOC, generics, bad design.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.