Page 1 of 1

Type() Complaints

Posted: 20 May 2014, 22:14
by [Shambles]
I experimented with Type() some today, with the following results:

Code: Select all

Type("")                    => String
Type("test")                => String
Type(0)                     => Integer
Type(0.5)                   => Float
Type(1.0)                   => Float
Type(True)                  => Integer
Type([1, 2])                => Object
Type({foo: "5", bar: "8"})  => Object
Type(UserDefinedInstance)   => Object
UserDefinedInstance.__Class => UserDefinedClass
Type(RegExMatchInstance)    => RegExMatchObject
Type(FuncInstance)          => Func
Type(FileInstance)          => FileObject
Type(ComInstance)           => ComObject
Type(ExceptionInstance)     => Object
ExceptionInstance.__Class   => ""
Most of this is what I expected.

I consider some of it wrong:
* Type() returns "Object" rather than the class for instances of user defined classes, even though the interpreter stored the type information in the "__Class" field.
* Type() returns "Object" and doesn't store anything in the "__Class" field for a exception.

Some of it is inconsistent:
* "Func" objects do not have a "Object" suffix like other objects.

Some of it is merely unfortunate:
* boolean values are still type-punned as integers
* arrays and dictionaries are still considered the same type

I hope v2 will be taken as a opportunity to clean up the type system.

I may have missed some built-in types (and therefore problems with Type()), since they aren't listed anywhere I can find in the documentation.

If you need a example of a situation where getting this right matters, imagine writing code to traverse a tree data structure stored as a array of arrays, with values of interest inside (as Lisp does with lists). It's vital to be able to tell whether a element is a "atom" or a "list" to descend into. I can't currently use Type() to do that, since it might mistake dictionaries, objects, or exceptions for arrays.

In general, though, it's important to be able to know the type of the data you're working with.

I hope these problems with Type(), and any I missed, will be fixed. It would be nice to have Type() backported to v1, assuming it works well.

Re: Type() Complaints

Posted: 20 May 2014, 22:23
by joedf
Functions aren't Objects... well, that's what i suppose.

Re: Type() Complaints

Posted: 20 May 2014, 22:44
by [Shambles]
The manual disagrees, as does the behavior of Func objects.

The help page starts with, and I quote:
"Func Object [v1.1.00+]

Represents a user-defined or built-in function which can be called by the script.

A reference to a Func object is also known as a function reference."

It continues on to describe the various methods on the object, like:
Func.IsByRef(ParamIndex)
... which does not look like a function call.

Re: Type() Complaints

Posted: 20 May 2014, 22:49
by guest3456
joedf wrote:Functions aren't Objects... well, that's what i suppose.
the docs may confuse you:
http://ahkscript.org/docs/Objects.htm wrote: Types of objects include:
  • Object - scriptable associative array.
  • File - provides an interface for file input/output.
  • Func - a function.
  • ComObject - wraps an IDispatch interface (a COM or "Automation" object).
perhaps the above should read: "Func - a function reference"

Code: Select all



funcname := "DllCall"

msgbox, % "'" funcname "' " (IsFunc(funcname) ? "is" : "is not") " a function"
msgbox, % "'" funcname "' " (IsObject(funcname) ? "is" : "is not") " an object"

funcref := Func(funcname)

msgbox, % "variable 'funcref' " (IsFunc(funcref) ? "is" : "is not") " a function"
msgbox, % "variable 'funcref' " (IsObject(funcref) ? "is" : "is not") " an object"

Re: Type() Complaints

Posted: 20 May 2014, 23:07
by [Shambles]
I don't think I'm confused. I'm familiar with programming in several languages that support object-oriented programming. Objects have methods. Functions don't.

In Python everything is a object, and function "references" have methods. They are still objects.

In C functions are "just" functions, and do not have methods (nor does anything else, though you can approximate it with cute tricks involving "structs" (record types) and function pointers).

AutoHotKey "Func Objects" are objects, since they have methods.

Re: Type() Complaints

Posted: 20 May 2014, 23:52
by joedf
Ok ok... i see the confusion here....
There is "Function" and there is "Function Object" -> not the same thing.

Re: Type() Complaints

Posted: 21 May 2014, 02:21
by lexikos
I consider some of it wrong:
* Type() returns "Object" rather than the class for instances of user defined classes, even though the interpreter stored the type information in the "__Class" field.
The user defined class name is not relevant to the purpose for which type() was added. It returns the internal type of a value. All objects of type "Object" support ObjInsert, ObjRemove, ObjMaxIndex, etc. and certain other functionality. "Object" is the exact (C++) class name of the object.
* Type() returns "Object" and doesn't store anything in the "__Class" field for a exception.
It took me a while to get what you're saying here. Type() shouldn't store anything anywhere or otherwise have any effect; it just returns a type name. I assume you mean that the Exception() function doesn't store anything in exception.__Class. That's because exception objects have no class; they're just associative arrays. __Class is only stored by the class syntax, in the actual class object, not objects derived from that class. Exception objects aren't derived from anything.

Note that you don't need to throw an object returned by Exception() as is. You can modify the object, construct an object yourself, or throw any other value of your choosing.
Some of it is inconsistent:
* "Func" objects do not have a "Object" suffix like other objects.
Of what value would consistency be in this case? Or more generally, of what value is the suffix?

If anything, I would prefer to remove the "Object" suffix wherever possible while still making sense. "Com" wouldn't make sense. "RegExMatch" wouldn't make sense. A "FileObject" relates to files, but isn't a file; it is an object for reading and writing files. I do not think "FuncObject" is clearer than "Func", and prefer to think of the value as "a reference to a function", not "a (reference to) a function object". A "Func" object isn't just an object containing information about the function, it is (one interface of) the actual data structure which defines the function.

One reason that the documentation uses "Func object" is that there is also a "Func function" (which returns a reference to a function).
Some of it is merely unfortunate:
* boolean values are still type-punned as integers
True and False are integers, 1 and 0, but one can use any type of value in a boolean expression. Type() just reflects how the language works in this instance.
* arrays and dictionaries are still considered the same type
There aren't "arrays" and "dictionaries", just objects which are used in either capacity. They aren't just considered the same type, both words are referring to the same thing, or different concepts, depending on your perspective.
It's vital to be able to tell whether a element is a "atom" or a "list" to descend into.
It seems your problem is that you want to differentiate between two data structures which are completely identical. If you want one object to be treated as a "list" and another object to be treated as an "atom", you simply need to store that information somewhere. Another (potentially more appropriate) model is to have the behaviour defined by the object being traversed via user-defined classes, instead of checking the type name at each step. There are already multiple ways you could solve this problem, without relying type().
I hope v2 will be taken as a opportunity to clean up the type system.
Been there and done that, and type() is the result (along with several other changes, documented under the heading "Types" in v2-changes).
guest3456 wrote:perhaps the above should read: "Func - a function reference"
No. A function reference is not a type of object; a function is a type of object, and a function reference is a reference to an object (where that object happens to be a function).

Re: Type() Complaints

Posted: 21 May 2014, 09:12
by joedf
Yep, dats some funky Func Functions. ;)

Re: Type() Complaints

Posted: 21 May 2014, 09:15
by jNizM

Code: Select all

funception()
{
    funception()
}

Re: Type() Complaints

Posted: 21 May 2014, 09:18
by joedf

Code: Select all

facepalm() {
    headhitwall() {
        flipthetable() {
            return facepalm()
        }
    }
}

Re: Type() Complaints

Posted: 21 May 2014, 19:23
by [Shambles]
There are two issues:
1. Type() returns incorrect values for instances of user-defined classes and exceptions.
2. The type system does not work as it should.

I stand by my assessment that it is incorrect to report a instance of a user-defined class or a exception as just "Object". Clearly either Chris or Lexicos has found it useful to differentiate between objects that are com objects, files, functions, and regular expressions. All are "objects" in the sense of a thing which contains state and has methods attached, but they have different semantics, and trying to use a function as a file will not work out well. There are other types that programmers want to use, and I don't believe it's desirable to have them operate on their data with a blindfold on.

I already gave one example (tree-walking) of where it helps to know the exact type of what you're dealing with.

Having a real boolean type allows you to do things like catch errors where you try to write a expression like 1 < x < 10 where x is 9001 which will silently evaluate to true in booleans-as-integers programming languages, and print booleans as booleans rather than integers. It is fine to allow converting between booleans and integers (useful when doing bitwise manipulation, like dealing with certain binary formats), but they should not be the same thing.

Having a real array type allows you to do things like check that an array (not something with string keys, for example) was passed to your procedure, and throw a exception with a meaningful error message if it was not, which might be useful when writing a library for public consumption, or an interpreter. I already gave a tree-walking example as well.

Having a real dictionary type mostly helps differentiate them from data structures that have array or object semantics. Again, I already gave the tree-walking example Using them well tends to require a set of facilities (e.g. the ability to list all the keys) that don't make sense when applied to arrays or instances of classes. When you "see" a dictionary it usually means the data within needs to be searched quickly, or indexed with something other than integers.

Differentiating behavior based on the type of a instance of a class is usually considered bad taste in dynamically typed programming languages, because it interferes with the benefits of polymorphism. However, those that want to write libraries for public consumption, or interpreters, or just generally want to assure the preconditions of a method are being obeyed, might enjoy being able to tell that they are working with a class of some sort, not a array or dictionary. "Calling" most dictionary's keys won't work, since values are unlikely to be functions.

I've seen all this type-punning before. Treating true and false as integers is widespread, unfortunately. Many modern statically type checked programming languages (e.g. ML, F#, Haskell, C#) and some dynamically type checked programming languages (e.g. Lisp from 1958, Python) have a separate type for them. Lua and JavaScript use dictionaries as arrays and objects. They are the only examples I'm aware of. Just because the AutoHotKey type system is as it is now does not mean that the laws of mathematics requires it to be so, or that it is the best of all possible designs. Since v2 is breaking compatibility to try to improve the programming language, it might be time to improve the type system.

It is, of course, possible, to work around all this. I can write my own Type() replacement that does something cute with static variables and memory addresses to recognize something Type() considers a "Object" as an exception, and failing that check to see if "__Class" is empty and if not report the type as that, and then enumerate all keys and if they are not part of the "generic set" on all objects assure they are all numeric without any gaps and if so report it as an array, and then there's dictionaries and so on... But this is all inefficient (very much so in the array-recognizing case). Or I could go OO-crazy and define my own types for everything and check "__Class" or the existence of certain custom methods everywhere... But this makes passing information to and from my code a pain due to the constant marshalling of data between AutoHotKey's basic types and my own personal Kingdom of Nouns. It all gives the appearance of plastering over cracks in a flawed design.

As to your criticism of my criticism of the inconsistent naming of the types returned by Type():
"Of what value would consistency be in this case?"
In my informed opinion it is inconsistency in design that must be defended, not consistency. So I ask you, what is the value of inconsistency in this case?
"Or more generally, of what value is the suffix?"
The value of the suffix is that it tells me it is a object, and thus has methods, unlike, for example, a integer. If AutoHotKey was a pure object-oriented programming language (e.g. like Smalltalk or Python), it would indeed not have any value.

I believed this forum was for constructive criticism of AutoHotKey v2. That's what this is. :)

Re: Type() Complaints

Posted: 21 May 2014, 23:14
by lexikos
The sort of type check you're asking for should not be done by comparing the type name, but by using the new "is" operator.

You are correct to post your criticisms here, but I am, of course, free to disagree.

I do not feel that a separate Boolean type or separate array/dictionary types are necessary for AutoHotkey.

Re: Type() Complaints

Posted: 28 May 2014, 17:02
by trismarck
[Shambles] wrote:I hope these problems with Type(), and any I missed, will be fixed.
There are also enumeration objects:
  • enum for AHK associative array objects
  • enum for COM objects

Re: Type() Complaints

Posted: 09 Jul 2014, 23:23
by [Shambles]
I wrote a library to (mostly) solve the problem in v1. Perhaps it will be more convincing than my arguments. If not, at least I have a partial workaround. "Repr" is still flawed due to the type system, though.

I posted on the forum about the library here: Plaster Introspection Library