We have all seen integer, floating point, string, and the occasional decimal type. What are some of the most strange or unique or useful types you have encountered, useful or not?
-
Hi user10008, welcome to Programmers.SE! Have you checked out our FAQ? Which of the six subjective guidelines do you think your question meets?– user8Commented Jan 25, 2011 at 0:04
-
4Would anyone like to write the entry for Lisp?– Mark CCommented Jan 25, 2011 at 1:59
-
I thought it might have been a dupe, but only because my answer would have been a dupe, so I'll post a link and maybe you'll find some good answers: programmers.stackexchange.com/questions/724/…– Peter TurnerCommented Jan 25, 2011 at 18:58
-
@Mark: I tried, but types are probably one of the least interesting things about Lisp.– Larry ColemanCommented Jan 25, 2011 at 21:36
-
@LarryC I thought this was the perfect question for Lisp because of the all-pervasive use of lists ! The lists form the syntax tree and this allows you to write functions that do amazing things to your code, I gather. I'm learning Racket (previously PLT Scheme) now. Lisp is the only programming language I have been truly motivated and interested to learn.– Mark CCommented Jan 26, 2011 at 4:45
24 Answers
I'll be short:
Maybe a
in Haskell.
With this simple construct, the language solves the issue of crashes or NullPointerException
, it neatly sidesteps the "One Million Mistake" of Tony Hoare :)
Frankly, an optional presence checked at compile-time ? It's dreamlike...
-
1
-
@Jonas: I must admit I don't like the
Option
name. Why notOptional
! It may be because I am no native speaker, butOption
does not convey the "optional" meaning to me. Commented Sep 27, 2011 at 7:01 -
The
Maybe
name is cute: "What do you have?" "Maybe an Int". However, the really neat bit is that it is both a functor and a monad which, in plain speak, means you get null-propagation for free. You never need to put null checks inside functions or in the middle of your code; you only ever need to check it at the very end of your code, if at all. Commented Jan 14, 2012 at 0:56 -
There's a
Maybe
monad for Ruby: lostechies.com/derickbailey/2010/10/10/the-maybe-monad-in-ruby Commented Jan 14, 2012 at 1:03
I am perennially fond of void *
. It's probably a symptom of something deeply flawed in me.
-
2Yes. I'm afraid that's exactly what it is. :) Oh, +1 for "interesting" rather than "unique". Objective-C obviously has
void *
and Pascal/Delphi havePointer
. Commented Jan 25, 2011 at 9:03 -
haha more of a non type, but you can't argue that it is not powerful Commented Jan 25, 2011 at 17:27
-
15I just love the inherent pessimism it expresses: 'Can you see that thing over there?' 'Yes, what is it?', 'No idea.'– biziclopCommented Jan 25, 2011 at 19:11
-
I always thought it was funny that you can't declare a void but you can take the address of it. Seems to me that with struct s { int A; void B; int C ; } that the address of B should be the address of the crack between A and C. But no, not allowed. Commented Sep 27, 2011 at 9:43
-
That's why in pascal "pointer" is used to denote a generic pointer, and not confuse with "procedure".– umlcatCommented Sep 27, 2011 at 15:42
Lua has a built-in table that is most impressive. It has a built-in hashtable and a vector, and with the use of metatables can be the fundamental base for object-oriented programming in a procedural language.
Each index of a table can receive any of the basic language structures (number, boolean, string, function -yes, functions are types on lua -, and tables).
-
Note how Javascript is built in a very similar way, and Python is built on the very same foundation, as probably is Ruby.– 9000Commented Jan 25, 2011 at 0:31
-
I think this is also possible in Perl and PHP, yes? Commented Jan 25, 2011 at 5:43
-
There is a difference between the tables in lua and the hash containers in other languages. There is a subtle implementation difference in the way lua distributes hash values that makes its tables work in ways that are almost magical. I mostly program in python, and on occasion find that I'm using assumptions that don't hold, based on my expectations of the way tables work in lua. A specific example of this magic is that integers hash to themselves + 1. This means that integer keys are packed densly, and that +0.0 and -0.0 have the same hash (they are equal) Commented Jan 25, 2011 at 6:10
I'm surprised nobody has mentioned Monads or Algebraic Datatypes yet.
Lisp has two interesting types: t
and nil
. What's interesting about them is that everything is a t
and nothing is a nil
.
Fortran has common blocks; it's one of the least common data types in modern languages, or, rather an unusual way to efficiently share data.
Fortran 95 has interval types and built-in interval arithmetics.
The list would not be complete without monadic types found in Haskell. To understand them you need a bit of effort.
-
Ah, UniData/UniVerse DBs have common blocks in their internal language (UniBasic) too. Commented Jan 25, 2011 at 0:39
-
Is a common block a block of code that is used by different parts of the program?– Mark CCommented Jan 26, 2011 at 4:52
-
1@MarkC IIRC its basically global data, but each function that accesses has to explicitly say it is going to at the top– jk.Commented Sep 27, 2011 at 8:32
I suppose it's really only strange coming from programming on a classical architecture, but certainly one of the hardest types for me to wrap my head around at first was the quantum register, which shows up in QCL.
PL/SQL lets you declare variables of type my_table.some_column%type
... I find that pretty damn useful.
And C# lets you declare objects as nullable or not, though I'm not sure that counts as a type.
-
4But
cursor%rowtype
is even funnier: it's a dynamically-formed record type that reflects which columns the cursor's query returns.– 9000Commented Jan 25, 2011 at 0:36 -
I had a soft spot in my heart for Euphoria's data types when I was younger
It is structured as thus:
Object
-> Atom
-> Sequence
- Atom = A single numeric value
Sequence = A sequence of Objects
-- examples of atoms: 0 98.6 -1e6 -- examples of sequences: {2, 3, 5, 7, 11, 13, 17, 19} {1, 2, {3, 3, 3}, 4, {5, {6}}} {{"jon", "smith"}, 52389, 97.25} {} -- the 0-element sequence
See: The Ref Manual
Note: "jon" is actually a short hand way of writing the sequence of ASCII values. For example "ABCDEFG"
is the same as {65, 66, 67, 68, 69, 70, 71}
-
7
-
-
1@FrustratedWithForms Same, I thought, "Hey, he said, 'Atom'! This looks like (a) Lisp but with unnecessary dividers. :P– Mark CCommented Jan 26, 2011 at 4:54
Felix has anonymous sum types. The type is written like:
typedef il = int + long;
as it would be in theory. The values are ugly:
case 0 of il (1)
case 1 of il (2L)
except perhaps for a unit sum such as 3 = 1 + 1 + 1
case 0 of 3
case 1 of 3
which unfortunately uses zero origin counting for "C compatibility". Anonymous sums are necessary for structurally typed algebraic types, for example:
(1 + T * li) as li
is a (singly linked) list of T. All other languages I know of required nominally typed sums, where both the type itself and the constructors must be given names.
The shorthand 3 used above is cute, the following is in the library:
typedef void = 0;
typedef unit = 1;
typedef bool = 2;
and this notation:
T ^ 3
is an array of static length 3 .. the 3 is not an integer but a sum of 3 units. What a pity + is not associative :)
q/kdb+ has tables built-in. Since it's a programming language and column-oriented database in one, there's no need for LINQ or ORMs.
For example, can create a table like this (assignment is distinquished by :
rather than =
as in most languages):
people:([]name:`Joe`Amy`Sarah; age:17 15 18; GPA:3.5 3.8 3.33)
Now I can look at my table:
q)show people
name age GPA
--------------
Joe 17 3.5
Amy 15 3.8
Sarah 18 3.33
And I can query it:
q)select from people where GPA>3.4
name age GPA
------------
Joe 17 3.5
Amy 15 3.8
I found union's in C++ to be 'quirky' when I first heard about them. I still haven't hit a scenario where they're the obvious choice to implement.
-
3Unions came from C. One good example is the zval struct in php. Commented Jan 25, 2011 at 7:12
-
2I've used them in a Z80 emulator, to easily access the 16 bit registers as whole registers (HL, BC) and as 8-bit registers (H, L, B and C). This reflects how they're used in Z80 asm. Also in "variants", a class that can hold a value of different types (for example int/float) - not sure why I didn't use subclasses, but it made sense at the time :) Commented Jan 25, 2011 at 11:50
-
@ggambett: I have done exactly the same for my Z80 programs! Only that I have also added a bitfield to access individual flags in the F register. Commented Sep 27, 2011 at 7:21
I'm still trying to wrap my head around what a multi-parameter function becomes in F# and other functional languages. Basically int f(Foo, Bar) becomes func f(Foo)
That is the two parameter function that takes a Foo, and a Bar and returns an int is really a one parameter function that takes a Foo and returns a one parameter function that takes a bar and returns an int. But somehow you can call it with two parameters if you want. I wrote a post about it here
-
8Rather, a function
f(Foo, Bar)
is the same as functionf(Foo)
that returns another functionf'(Bar)
which returns the value whatf(Foo, Bar)
would return. That is, if you fix the 'Foo' argument, but not 'Bar', you have a function that does not depend on 'Foo' but still depends on the 'Bar' argument. This is typical for functional languages; it's called 'currying'.– 9000Commented Jan 25, 2011 at 0:40
Regular Expressions:
They are extremely powerful yet compact objects.
Languages that have them built in have great ability to manipulate text (lets not hear the word parse they are not that good).
-
2It is entirely possible to parse many simple grammars with regexes. For instance, it's relatively trivial to parse an ini file with a minimum of logic on top of a set of regexes. The mistake a lot of people make is trying to parse very complex grammars with it (ie. XML/HTML). Commented Jan 25, 2011 at 5:33
-
-
@Mark C: The top is answer is (with a record breaking 4320 up votes). You can't Commented Jan 26, 2011 at 5:37
-
Yes, that was for humor. It sprang to mind when I read Matthew's comment.– Mark CCommented Jan 26, 2011 at 5:56
A handful of languages in the functional family have a class of types known as Unity. The distinguishing feature of Unity types are that they contain no information, they are zero bit types. A unity type (in some variations) is also its only value, or (in most others) has only one value (that is not itself a type).
These are useful, though, because they are distinguished types. Since you can't implicitly convert from one unity type to another, you can put static type checking to work in a very efficient, and expressive way.
Unity is also the way most such languages describe Enums, by allowing a new type to be any of a defined set of other types, or to describe maybe types, values that may be either a value of a typical type (say, an integer), or have a value that represents no-value.
Some languages that don't employ the richness of user defined unity types still have unity in them, in some form or another. For instance, Python has at least three unity types, NoneType
, NotImplementedType
, and EllipsisType
. It's interesting that the first two both mean something like "No value", but the third is used in complex values (specifically, slice expressions) to represent interesting special cases.
Other interesting examples of unity include NULL
in sql and undefined
in javascript, but not void
in C or C++. void
fails. Even though it describes a value of no information, but no actual value can be of type void
.
Ruby's symbol
type is a bit unusual. It's essentially a string implementing the singleton pattern. Or something. So far, I've found the best uses for symbols are in tracking states and passing function names.
-
-
Well, it's not really that unusual. Ruby inherited it from Smalltalk, which inherited it from Lisp. Scala also has it, I think. In fact, almost every language implementation (compiler or interpreter) has a symbol table internally, Lisp, Smalltalk and Ruby just expose it to the programmer. Commented Apr 13, 2011 at 23:46
COBOL. Essentially only two basic data types, strings and numbers, but you have to specify exactly how they're laid out in memory, e.g. PIC S9(5)V99 COMP-3
.
-
I can beat that. BCPL has one data type - word; see en.wikipedia.org/wiki/BCPL Commented Jan 25, 2011 at 5:20
-
There are different types of numbers (COMP, COMP-1, COMP-2, COMP-3). Commented Jan 25, 2011 at 18:47
-
That sounds awful. Can you elaborate on what those details mean?– Mark CCommented Jan 26, 2011 at 4:51
-
S
= signed,9(5)
= 5 digits,V
= implicit decimal point,99
= 2 more digits,COMP-3
= BCD + sign nybble.– dan04Commented Jan 26, 2011 at 5:28
Clipper had 'Code Blocks', which were similar to anonymous methods. They could be passed around and evaluated as needed, usually as a form of a callback. You'd often use them for things like performing calculations on the fly when presenting tables of data.
VHDL has physical types. A literal of such type includes both a value and a unit. You can define subunits as well. For instance, a predefined physical type is time
:
type time is range <machine dependant> to <machine dependant>
units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
Ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units;
Together with operator overloading, you can define very interesting things.
Clojure is interesting because it has a meta-concept of "abstractions" that pervade the language. Examples:
- Collections
- Sequences (lazy and non-lazy)
- Higher Order Functions
- Multimethods
- Protocols
- Managed references
- Macros
- various others.....
To some extent, the abstractions take the "single responsibility principle" to the extreme. It's up to you to compose them to get the functionality that you want, but you can be extremely flexible about how you glue them together.
For example, if you want a class-based OOP system with inheritance, you could build one out of these core abstractions relatively quickly.
In practice, the abstractions themselves are designed in a way that multiple implementations are possible, e.g. through specific interfaces like clojure.lang.ISeq for sequences or clojure.lang.IFn for higher order functions.
There's an interesting video about this topic: The Art of Abstraction
If you want a language with a unique type then head for BCPL. This language only has one data type, the word, being a fixed number of bits for the language implementation.
Googles Go has a "Channel" type which is quite unique.
-
1Channels aren't unique. Many languages have them. Felix had them 10 years before Google existed :) Ocaml had them 10 years before Felix existed.– YttrillCommented Jan 13, 2012 at 20:13
-
And there was at least one other language which had channels before Ocaml existed. Still one of the least available types in programming languages.– BrainlagCommented Jan 14, 2012 at 11:43