Gå til innhold

Hva er poenget med dynamiske datatyper?


Anbefalte innlegg

Går jo an. Dette er mulig i C#. Men som regel så vil du bare bruke statisk typesetting, ettersom det er lite eller ingen jobb for programmereren, men gjør det absolutt 100% klart at programmet skal bruke int32 istedet for bigint eller omvendt, som er en antagelse compileren umulig kan gjøre korrekt for deg compile time.

Lenke til kommentar
Videoannonse
Annonse

Ja, eller en kan velge å begynne i andre enden. D.v.s. at en starter med at alle variabler er dynamiske eller ikke har noen bestemt type deklarert for seg -- så legger kompileren til det den er sikker på selv, og tilslutt kan brukeren legge til typedeklarasjoner (og annet også) om han/hun ønsker dette. "Optimize last". Da vil kompileren generere kode som ikke gjør run-time typesjekking.

 

Fungerer glimrende i mange språk det. F# er det språket jeg kjenner som kjører den helt ut, der nesten alt er utledet. Er jo det f.eks PyPy gjør for å få Python til å gå fort (Så kan man diskutere om RPython + PyPy er statisk eller dynamisk typet, skillelinjene begynner å bli litt fuzzy der).

 

Fungerer fint for mange språk det. Dårligere for andre, i mange dynamiske språk er typen verdiavhengig. Hvis f.eks en int64 kan overflowe inn i en bigint eller en eller annen kompleks datatype må det nødvendigvis alltid legges på en sjekk på type/overflow uansett hva input var. Kanskje en divisjon mellom to heltall returnerer heltall hvis divisjonen går opp, eller en eller annen kompleks type hvis den ikke gjør det, da må det inn type og verdi-sjekker alle steder det brukes divisjon, osv...

Endret av MailMan13
Lenke til kommentar
Gjest Slettet+9871234

Jeg starter her:

 

Men det er ikke eneste grunnen til å hevde at dynamiske datatyper er noe tull: dynamiske datatyper fører ofte med seg noe annet jeg synes er tullete: implisitt konvertering av datatyper.

 

og leser meg gjennom tråden.

 

Ulike språk har ulike egenskaper etter min mening. Få språk er så strenge som Simula:

 

"But after a while, most programmers realize that this means that a program is equipped with a safety net: many errors that programmers make when they construct programs are caught by this net before they lead to unpleasant effects. An example: A very expensive American space rocket crashed on its way to Venus a few years ago, because of an extremely trivial error in a FORTRAN program. A comma had be written as a point, and, as a consequence of that, the start of a special kind of repeat imperative was mistakenly read as an assignment imperative assigning a value to an undeclared variable. Had it been required to declare every variable in FORTRAN programs, the compiler would have discovered that the variable was undeclared and the error would have been caught much earlier than in the Atlantic Ocean."

 

Professor Bjørn Kirkerud (1989): "Object Oriented Programming With Simula". Addison Wesley Publishing Company ISBN 0 201 17574 6. Page 31-32.

 

PHP er som kjent tolket og løst typet. Jeg ser åpenbare fordeler med at variable kan endres ved runtime i PHP basert web kode. Jeg ser også åpenbare sikkerhetrisiko ved det.

 

C er statisk typet og selvfølgelig sikrere av den grunn.

 

Så min konklusjon: Alt avhenger av formålet. Alt dreier seg ikke bare om hurtighet og sikkerhet.

 

Leser videre i tråden og håper ikke jeg gjentar allerede publiserte argumenter.

Endret av Slettet+9871234
Lenke til kommentar
Gjest Slettet+9871234

Jeg har aldri kommet over "Oi! Nå hadde det vært kjekt med dynamiske datatyper". Ikke en eneste gang. Der hvor det hadde vært kjekt, tar generics og templates over på en mer elegant måte.

Men generics er ganske dustete i Java (type erasure), så vi ser bortifra det.

 

* Typesystemene i Java/C regnes vel ikke som særlig gode blandt de som holder på med type-teori, uten at jeg har særlig å backe opp det med selv. Merker forsåvidt tendensene selv når jeg har prøvd å løse noen "dynamiske" problemere i henholdsvis Java og Maude.

Til dette vil jeg anbefale den klassiske boken:

 

Gordon Blair, John Gallagher, David Hutchison and Doug Shephard (1991): "Object Oriented Languages, Systems and Applications." Pitman Publishing ISBN 0-273-03132-5

 

Noen sentrale sitater:

 

Side 87:

 

Two types are the same if they provide the same behaviours.

 

Type is not Class.

 

Class is fundamentally about implementation whereas type is concerned with abstract behaviour.

Two objects of the same class will be of the same type. However, it is not necessarily the case that two objects of the same type will be of the same class.

 

Se spesielt kapittel 4.

 

Basic concepts III (Types, Abstract Data Types and Polymorphism).

Endret av Slettet+9871234
Lenke til kommentar

Ja, eller en kan velge å begynne i andre enden. D.v.s. at en starter med at alle variabler er dynamiske eller ikke har noen bestemt type deklarert for seg -- så legger kompileren til det den er sikker på selv, og tilslutt kan brukeren legge til typedeklarasjoner (og annet også) om han/hun ønsker dette. "Optimize last". Da vil kompileren generere kode som ikke gjør run-time typesjekking.

 

Fungerer glimrende i mange språk det. F# er det språket jeg kjenner som kjører den helt ut, der nesten alt er utledet. Er jo det f.eks PyPy gjør for å få Python til å gå fort (Så kan man diskutere om RPython + PyPy er statisk eller dynamisk typet, skillelinjene begynner å bli litt fuzzy der).

 

Fungerer fint for mange språk det. Dårligere for andre, i mange dynamiske språk er typen verdiavhengig. Hvis f.eks en int64 kan overflowe inn i en bigint eller en eller annen kompleks datatype må det nødvendigvis alltid legges på en sjekk på type/overflow uansett hva input var. Kanskje en divisjon mellom to heltall returnerer heltall hvis divisjonen går opp, eller en eller annen kompleks type hvis den ikke gjør det, da må det inn type og verdi-sjekker alle steder det brukes divisjon, osv...

F# deduserer typer, det er ikke dynamisk typet. Jeg ville nesten tro at alle funksjonelle språk er statisk typet, ettersom dynamisk typesetting ikke gir mening i et språk som tross alt ikke teknisk sett tillater variabler i utgangspunktet.

Jeg er dog ikke noen ekspert på funksjonelle språk, og vet svært lite om det utover det lille jeg har tuklet med Haskell og F#.

Lenke til kommentar

F# deduserer typer, det er ikke dynamisk typet. Jeg ville nesten tro at alle funksjonelle språk er statisk typet, ettersom dynamisk typesetting ikke gir mening i et språk som tross alt ikke teknisk sett tillater variabler i utgangspunktet.

Jeg er dog ikke noen ekspert på funksjonelle språk, og vet svært lite om det utover det lille jeg har tuklet med Haskell og F#.

Fullt klar over at F# er statisk. Skulle kanskje vært litt tydligere på poenget dog.

 

Konklusjonen som var meningen å få frem er at det er ikke tilstedeværelsen av mange typenavn i kildekoden som gjør et språk statisk eller dynamisk. Selv om det er det som er den umiddelbare forsjkellen mellom språkene som vanligvis trekkes frem (Java/C++ vs Python). Bruker man et typesystem som ikke er like "broken beyond repair" som Java sitt så er det ikke alltid slik lenger.

Lenke til kommentar

Ja, eller en kan velge å begynne i andre enden. D.v.s. at en starter med at alle variabler er dynamiske eller ikke har noen bestemt type deklarert for seg -- så legger kompileren til det den er sikker på selv, og tilslutt kan brukeren legge til typedeklarasjoner (og annet også) om han/hun ønsker dette. "Optimize last". Da vil kompileren generere kode som ikke gjør run-time typesjekking.

Fungerer fint for mange språk det. Dårligere for andre, i mange dynamiske språk er typen verdiavhengig. Hvis f.eks en int64 kan overflowe inn i en bigint eller en eller annen kompleks datatype må det nødvendigvis alltid legges på en sjekk på type/overflow uansett hva input var. Kanskje en divisjon mellom to heltall returnerer heltall hvis divisjonen går opp, eller en eller annen kompleks type hvis den ikke gjør det, da må det inn type og verdi-sjekker alle steder det brukes divisjon, osv...

 

Ja, eller en kan si at en ikke ønsker slik sikkerhet eller oppførsel; det er opp til en selv hva som er viktig, eller hva dette "er" (sikkerhet/oppførsel), eller ikke for en i en gitt sammenheng. En kan si at en ønsker samme oppførsel som i C ("hjernedød" roll-around ved overflow), og kompileren vil da generere ennå enklere, "kjappere", kode.

Endret av worseisworser
Lenke til kommentar
Gjest Slettet+9871234

Ingen dynamisk kode nødvendig. Ettersom alle objekter implementerer Equals og operator == så er det ikke nødvendig å vite datatypen på feltene.

 

Mikser dere ikke type med klasse, pholymorfi og overloading? Jfr. min foregående post.

 

Sidenote:

Merk at == er forskjellig fra === i Php.

Endret av Slettet+9871234
Lenke til kommentar

Hva hvis en istedet for datatyper, heller definerer gyldige områder?

 

Slik for eksempel:

 

my_var = 0 constrain {0 -> 100}
my_big = 0 constrain bigint
my_person = null constrain person

 

Og hvis en ikke sier noe annet, blir det dynamisk typet.

Endret av GeirGrusom
Lenke til kommentar

Ja, eller en kan si at en ikke ønsker slik sikkerhet eller oppførsel; det er opp til en selv hva som er viktig, eller hva dette "er" (sikkerhet/oppførsel), eller ikke for en i en gitt sammenheng. En kan si at en ønsker samme oppførsel som i C ("hjernedød" roll-around ved overflow), og kompileren vil da generere ennå enklere, "kjappere", kode.

Er ikke kjent med noen dynamiske språk som lar meg bestemme det, hvilke?

 

Dem jeg har vært borti gjør stort sett auto-overflow inn i double uansett, så må man inn med floor/ceil-funksjoner om man vil være sikker på hva man har med å gjøre.

Endret av MailMan13
Lenke til kommentar

Ja, eller en kan si at en ikke ønsker slik sikkerhet eller oppførsel; det er opp til en selv hva som er viktig, eller hva dette "er" (sikkerhet/oppførsel), eller ikke for en i en gitt sammenheng. En kan si at en ønsker samme oppførsel som i C ("hjernedød" roll-around ved overflow), og kompileren vil da generere ennå enklere, "kjappere", kode.

Er ikke kjent med noen dynamiske språk som lar meg bestemme det, hvilke?

 

Dem jeg har vært borti gjør stort sett auto-overflow inn i double uansett, så må man inn med floor/ceil-funksjoner om man vil være sikker på hva man har med å gjøre.

 

CL-USER> (values (lisp-implementation-type)
                (lisp-implementation-version))
"SBCL"
"1.0.47.30"

CL-USER> (defun sum (x y)
          (+ x y))
SUM

CL-USER> (sum 4 3)
7

CL-USER> (disassemble 'sum)
; disassembly for SUM
WARNING: bogus form-number in form!  The source file has probably 
been changed too much to cope with.
; 037CA30D:       498B442438       MOV RAX, [R12+56]          ; no-arg-parsing entry point
;       12:       4883C010         ADD RAX, 16
;       16:       48C740F852000000 MOV QWORD PTR [RAX-8], 82
;       1E:       488968F0         MOV [RAX-16], RBP
;       22:       4989442438       MOV [R12+56], RAX
;       27:       4981BC24C800000017001020 CMP QWORD PTR [R12+200], 537919511
;       33:       7402             JEQ L0
;       35:       CC0F             BREAK 15                   ; single-step trap (before)
;       37: L0:   488B55F8         MOV RDX, [RBP-8]
;       3B:       488B7DF0         MOV RDI, [RBP-16]
;       3F:       4C8D1C25E0010020 LEA R11, [#x200001E0]      ; GENERIC-+
;       47:       41FFD3           CALL R11
;       4A:       480F42E3         CMOVB RSP, RBX
;       4E:       498B442438       MOV RAX, [R12+56]
;       53:       48C740F000000000 MOV QWORD PTR [RAX-16], 0
;       5B:       48C740F800000000 MOV QWORD PTR [RAX-8], 0
;       63:       4883E810         SUB RAX, 16
;       67:       4989442438       MOV [R12+56], RAX
;       6C:       488BE5           MOV RSP, RBP
;       6F:       F8               CLC
;       70:       5D               POP RBP
;       71:       C3               RET
;       72:       CC0A             BREAK 10                   ; error trap
;       74:       02               BYTE #X02
;       75:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       76:       54               BYTE #X54                  ; RCX
NIL

CL-USER> (defun sum (x y)
          (declare (optimize (speed 3) (safety 0))
                   (fixnum x y))
          (the fixnum (+ x y)))
STYLE-WARNING: redefining COMMON-LISP-USER::SUM in DEFUN
SUM

CL-USER> (disassemble 'sum)
; disassembly for SUM
; 03AAFF4F:       4801FA           ADD RDX, RDI               ; no-arg-parsing entry point
;       52:       488BE5           MOV RSP, RBP
;       55:       F8               CLC
;       56:       5D               POP RBP
;       57:       C3               RET
NIL

CL-USER> ;; ingen run-time typesjekking eller overflowsjekking o.l.
; No value

CL-USER> (sum 4 3)
7

CL-USER> (sum most-positive-fixnum 1)
-1152921504606846976

CL-USER> most-positive-fixnum
1152921504606846975

CL-USER> most-negative-fixnum
-1152921504606846976

CL-USER> 

 

edit:

Det er kombinasjonen av det å si at en ønsker full speed (speed 3) og ingen safety (safety 0) som gjør at kompileren genererer slik kode.

Endret av worseisworser
Lenke til kommentar

Burde nesten kunne gjettet at Lisp støttet noe sånt, stilig ;)

 

Utledes x og y når du kaller den, eller er det helt statisk? Ser ut som du får ut asm med integer-add før du har kalt den, så funksjonen kanskje helt statisk typet?

 

Denne er helt statisk.

 

En har tilgang til compiler macroer som gjør at en kan dispatche til (eller generere blir vel egentlig mer riktig å si) forskjellige versjoner av SUM basert på type-informasjon som enten er eksplisitt tilgjengelig fra brukerens side eller inferret på andre vis, men dette er noe begrenset i SBCL desverre -- selv om Lisp som standard ligger åpen m.t.p. hva som er mulig å legge til her, altså som implementør av språket.

 

Synes uansett blandingen av det statiske og det dynamiske er spennende, men spesielt med det dynamiske som utgangspunkt. :) Litt synd det ikke er mer utbredt -- etter hva jeg vet ..

Lenke til kommentar

F# er hakket mer elegant siden den utleder det selv, for.eks kan man ha en funksjon:

let sum(a, b) = a + b

Men her lager ikke kompileren noe kode, men når man kommer videre:

 

let c = sum(1.0, 2)

Så kommer det ut en overload (int, double) som returnerer en double (siden int + double blir double).

 

Hvis man senere plugger inn:

let d = sum("abc", "123")

Så kommer det ut en overload (string, string) som returnerer en string. Osv...

 

Veldig behagelig måte å kode på, så lenge operatorene som er brukt er definert for argumentene man sender inn er det greit. Veldig kompakt syntaktisk og fremdeles 100% statisk.

 

Det faller desverre litt sammen på metodekall, uten at jeg ser noe god grunn til at det ikke skulle fungere på samme måte. For eks. ser jeg ikke noe grunn til at jeg ikke bør kunne få si "let fn (x) = x.Length()" så lenge jeg bare kaller funksjonen med argumenter som har Length(), burde ikke være noe vanskligere å få til :(

Endret av MailMan13
Lenke til kommentar

F# er hakket mer elegant siden den utleder det selv, for.eks kan man ha en funksjon:

let sum(a, b) = a + b

Men her lager ikke kompileren noe kode, men når man kommer videre:

 

let c = sum(1.0, 2)

Så kommer det ut en overload (int, double) som returnerer en double (siden int + double blir double).

 

Hvis man senere plugger inn:

let d = sum("abc", "123")

Så kommer det ut en overload (string, string) som returnerer en string. Osv...

 

Veldig behagelig måte å kode på, så lenge operatorene som er brukt er definert for argumentene man sender inn er det greit. Veldig kompakt syntaktisk og fremdeles 100% statisk.

 

Ah, ok. SBCL vil gjøre det samme når funksjonen (sum i dette tilfellet) er deklarert inline, men den har noen litt kjedelige begrensninger (edit: ganske like de du nevner helt til slutt etter redigering egentlig).

 

Har noen tittet på Scala forresten?

Endret av worseisworser
Lenke til kommentar
Gjest Slettet+9871234

F# er hakket mer elegant siden den utleder det selv, for.eks kan man ha en funksjon:

I C++ (om man ser stort på det, som en "supermengde av C") løses det enkelt ved templates og overloading, men det har vel med klasser og polymorfi og ikke datatype å gjøre.

 

Egenproduserte "datatyper" klasser er ikke datatype. For å gjenta:

 

Type is not Class.

 

Ytterligere komplikasjoner oppstår ved subtyping. Se boken nevnt i min andre (#44) post.

Endret av Slettet+9871234
Lenke til kommentar

F# er hakket mer elegant siden den utleder det selv, for.eks kan man ha en funksjon:

I C++ (om man ser stort på det, som en "supermengde av C") løses det enkelt ved templates og overloading,

 

Siden dette er enkelt i C++; kan du poste litt kode?

Endret av worseisworser
Lenke til kommentar

I C++ (om man ser stort på det, som en "supermengde av C") løses det enkelt ved templates og overloading,

Vil påstå at det (statisk utleding av typeinformasjon vs dynamiske typing) ikke er løst i C++ siden man, med et unntak jeg kommer på, eksplisitt må mate inn typenavn hele veien. Funksjonelle språk er alle funksjoner generiske/templates "by default", så man slipper navnespagettien som jager folk bort fra C++ og Java.

 

C++ templates er ikke bundet til polimorfi spesielt. Man har litt covariance med templates som benytter de generelle reglene i C++. Arv og polimorfi er en av mekanismene man oppnår det med, men man kan like gjerne si:

template <typename T>
const T& max(const T& x, const T& y)
{
   return x > y ? x : y;
}

 

Og kalle den opp med:

double num = max<double>(1, 2.0);

Ingen klasser involvert der, men vi kan sende inn noe annet enn T så lenge det er covariant i T.

 

Egenproduserte "datatyper" klasser er ikke datatype. For å gjenta:

 

Type is not Class.

 

Ytterligere komplikasjoner oppstår ved subtyping. Se boken nevnt i min andre (#44) post.

 

Du leser det som om det står "Class is not Type"?

Endret av MailMan13
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...