• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. Scheme vs Common Lisp style in SKILL++

Stats

  • Locked Locked
  • Replies 18
  • Subscribers 144
  • Views 22564
  • Members are here 0
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Scheme vs Common Lisp style in SKILL++

tweeks
tweeks over 11 years ago

 While you can write your SKILL++ code like it is C, Maclisp, Scheme, or Common Lisp (or none of the above...), I've been experimenting with Common Lisp style lately.

Trying to write Common Lisp style code in SKILL++ can lead to curious situations like this:

(defvar first car)

The intention is to define FIRST as another name for CAR, as in Common Lisp.  In Scheme, we would write

(define first car)

which seems pretty natural, but using DEFVAR to define a function just feels.... wrong... somehow... :)

I guess I should use ALIAS:

 (alias second cadr)

 except ALIAS has weird limitations:

ILS-> (foo = first)
primop:car
ILS-> (foo '(1 2 3))
1
ILS-> (foo = second)
macro:evalalias
ILS-> (foo '(1 2 3))
*Error* evalalias: unknown alias - foo

  • Cancel
Parents
  • Andrew Beckett
    Andrew Beckett over 10 years ago

    Lot's of things to answer here:

    tweeks said:
    It sounds like aliases are basically depricated now.

    No. Aliases are a SKILL mode thing rather than a SKILL++/Scheme mode thing - and in SKILL mode they can't be assigned anyway, in the same way that functions can't be assigned. So they are not deprecated.

    tweeks said:
    What is SKILL/SKILL++'s philosophy? Scheme is minimalist and favors a functional style. Common Lisp is large and favors iteration over recursion. SKILL is large like Common Lisp, and seems to favor iteration like Common Lisp (foreach's default is mapc rather than mapcar; the gdmSpec interface is stateful; tail call optimization was not added until quite recently, and it still doesn't work reliably (for me anyway)...), but its documentation makes a big deal about being compatible with Scheme, and SKILL++ (with the plus-plus suggesting incremental improvement over the original) moved in the direction of Scheme's evaluation semantics rather than Common Lisp's, yet it added an object system patterned after CLOS!

    Remember that SKILL is a pragmatic implementation where features are added because of demand rather than necessarily for idealistic reasons. A lot of the way it is are because of:

    1. history
    2. ensuring compatibilty - we do not want to break code that may have existed for 20 years

    On the history front, as you know, SKILL originated from a small LISP, Franz LISP. So it pre-dates both Common LISP and Scheme. Originally it was an interpreted language, which changed into a byte-code compiled (VM) language in the 4.3 release (in 1993, if my memory is correct). Around that time, there was an organization called CFI - the CAD Framework Initiative (I think) which had a goal of having Scheme as the customization language for lots of CAD tools. The "ils" mode of SKILL was an attempt to provide Scheme-like features within SKILL, without losing compatibility. There was initially a more aggressive Scheme implementation too, but the decision was made to adopt some of the concepts without the strict language compatibility with Scheme (such as case insensitivity, no infix operators, etc etc). At the same time there was an internal demand for an object system, primarily for the Analog Design Environment (aka Analog Artist) which had a home-grown object system but needed to be developed. This object system was based on CLOS, and so that was a logical choice for the implementation in SKILL. What was implemented was a much reduced version of CLOS, which only supported single inheritance, no multi-methods, and a few other restrictions. It wasn't deemed cost-effective to implement the whole of CLOS or other missing features from Common LISP on the off-chance that somebody might need it, when the vast majority of people writing SKILL were not programmers and certainly know little of either Scheme or Common LISP.

    As time went by, a few people asked for more Common LISP features to make their code simpler. Some of these add little value other than a different way of doing things - but some do add a valuable difference. For example, a number of the additions which came in for IC615 just add another way of doing something that was there already - flet and labels are the two I immediately debated their value. However, I have found myself using flet in the case where I intentionally wanted to wrap an existing function - doing this with nested defun/procedure feels a bit clunky. I'm so used to using nested defun/procedure because it's been there since 1995 when ils mode was added, that I didn't see any benefit in adoption some common LISP form that I'd not really used for many years. But flet has it's benefits. For example - here's an example I gave during a training class:

    let(((realarrayref arrayref))
      procedure(arrayref(arr ind)
        realarrayref(arr ind) || setarray(arr ind makeTable('tab nil))
      )
      a=makeTable('tab nil)
      a["hello"]["world"]["andrew"]=123
    )

    It shows a hash table that can have dimensions added automatically (a bit like perl - which was what the user on the course was saying was an advantage of perl that SKILL didn't have). It's in C-like syntax because it was an illustration for a user more comformatable with C syntax.

    However, with flet this clearer - none of this having to "rename" the arrayref via a local variable:

    flet(((arrayref (arr ind) arr[ind] || (arr[ind]=makeTable('tab nil))))
      a=makeTable('tab nil)
      a["hello"]["world"]["andrew"]=123
    )

    And then I can make this into a nice macro:

    defmacro(WithAutoHash (@rest body)
     `flet(((arrayref (arr ind) arr[ind] || (arr[ind]=makeTable('tab nil))))
       ,@body
      )
    )

    procedure(TrExampleArray()
      let(((arr makeTable('tab nil)))
        WithAutoHash(
          arr["my"]["big"]["test"]=7
          arr["my"]["other"]["test"]=9
          arr
        )
      )
    )

    However, I never find myself using labels. Two reasons - I find the name of the function counterintuitive - nothing in the name suggests to me that this defines functions using letrec semantic; secondly I can do all that just by declaring nested defuns.

    So with all of this, your mileage may vary. Coming up with a consistent style is a bit of a personal choice, as I think it is with any language. I have my own rules that I don't always stick to - for example, I tend to write most of my code in LISP-syntax, and I am pretty anal about not using operators but using functions - so I will write (times (plus a b) (difference a b)) rather than (a+b)*(a-b). I also tend to use defun when writing in LISP syntax. I won't mix and match LISP syntax and C syntax in the same file, but I will use C syntax if I know the target audience is more likely to want C syntax and also may want to understand and adapt the code - but then I use procedure rather than defun (usually). No good reasons for some of these, apart from trying some level of consistency. I never use define to define functions - only to define variables. And I never use defvar at all.

    I've learned that trying to suggest a set of global style guidelines is a thankless task which nobody will agree to (and quite probably I wouldn't agree to them myself!)

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Reply
  • Andrew Beckett
    Andrew Beckett over 10 years ago

    Lot's of things to answer here:

    tweeks said:
    It sounds like aliases are basically depricated now.

    No. Aliases are a SKILL mode thing rather than a SKILL++/Scheme mode thing - and in SKILL mode they can't be assigned anyway, in the same way that functions can't be assigned. So they are not deprecated.

    tweeks said:
    What is SKILL/SKILL++'s philosophy? Scheme is minimalist and favors a functional style. Common Lisp is large and favors iteration over recursion. SKILL is large like Common Lisp, and seems to favor iteration like Common Lisp (foreach's default is mapc rather than mapcar; the gdmSpec interface is stateful; tail call optimization was not added until quite recently, and it still doesn't work reliably (for me anyway)...), but its documentation makes a big deal about being compatible with Scheme, and SKILL++ (with the plus-plus suggesting incremental improvement over the original) moved in the direction of Scheme's evaluation semantics rather than Common Lisp's, yet it added an object system patterned after CLOS!

    Remember that SKILL is a pragmatic implementation where features are added because of demand rather than necessarily for idealistic reasons. A lot of the way it is are because of:

    1. history
    2. ensuring compatibilty - we do not want to break code that may have existed for 20 years

    On the history front, as you know, SKILL originated from a small LISP, Franz LISP. So it pre-dates both Common LISP and Scheme. Originally it was an interpreted language, which changed into a byte-code compiled (VM) language in the 4.3 release (in 1993, if my memory is correct). Around that time, there was an organization called CFI - the CAD Framework Initiative (I think) which had a goal of having Scheme as the customization language for lots of CAD tools. The "ils" mode of SKILL was an attempt to provide Scheme-like features within SKILL, without losing compatibility. There was initially a more aggressive Scheme implementation too, but the decision was made to adopt some of the concepts without the strict language compatibility with Scheme (such as case insensitivity, no infix operators, etc etc). At the same time there was an internal demand for an object system, primarily for the Analog Design Environment (aka Analog Artist) which had a home-grown object system but needed to be developed. This object system was based on CLOS, and so that was a logical choice for the implementation in SKILL. What was implemented was a much reduced version of CLOS, which only supported single inheritance, no multi-methods, and a few other restrictions. It wasn't deemed cost-effective to implement the whole of CLOS or other missing features from Common LISP on the off-chance that somebody might need it, when the vast majority of people writing SKILL were not programmers and certainly know little of either Scheme or Common LISP.

    As time went by, a few people asked for more Common LISP features to make their code simpler. Some of these add little value other than a different way of doing things - but some do add a valuable difference. For example, a number of the additions which came in for IC615 just add another way of doing something that was there already - flet and labels are the two I immediately debated their value. However, I have found myself using flet in the case where I intentionally wanted to wrap an existing function - doing this with nested defun/procedure feels a bit clunky. I'm so used to using nested defun/procedure because it's been there since 1995 when ils mode was added, that I didn't see any benefit in adoption some common LISP form that I'd not really used for many years. But flet has it's benefits. For example - here's an example I gave during a training class:

    let(((realarrayref arrayref))
      procedure(arrayref(arr ind)
        realarrayref(arr ind) || setarray(arr ind makeTable('tab nil))
      )
      a=makeTable('tab nil)
      a["hello"]["world"]["andrew"]=123
    )

    It shows a hash table that can have dimensions added automatically (a bit like perl - which was what the user on the course was saying was an advantage of perl that SKILL didn't have). It's in C-like syntax because it was an illustration for a user more comformatable with C syntax.

    However, with flet this clearer - none of this having to "rename" the arrayref via a local variable:

    flet(((arrayref (arr ind) arr[ind] || (arr[ind]=makeTable('tab nil))))
      a=makeTable('tab nil)
      a["hello"]["world"]["andrew"]=123
    )

    And then I can make this into a nice macro:

    defmacro(WithAutoHash (@rest body)
     `flet(((arrayref (arr ind) arr[ind] || (arr[ind]=makeTable('tab nil))))
       ,@body
      )
    )

    procedure(TrExampleArray()
      let(((arr makeTable('tab nil)))
        WithAutoHash(
          arr["my"]["big"]["test"]=7
          arr["my"]["other"]["test"]=9
          arr
        )
      )
    )

    However, I never find myself using labels. Two reasons - I find the name of the function counterintuitive - nothing in the name suggests to me that this defines functions using letrec semantic; secondly I can do all that just by declaring nested defuns.

    So with all of this, your mileage may vary. Coming up with a consistent style is a bit of a personal choice, as I think it is with any language. I have my own rules that I don't always stick to - for example, I tend to write most of my code in LISP-syntax, and I am pretty anal about not using operators but using functions - so I will write (times (plus a b) (difference a b)) rather than (a+b)*(a-b). I also tend to use defun when writing in LISP syntax. I won't mix and match LISP syntax and C syntax in the same file, but I will use C syntax if I know the target audience is more likely to want C syntax and also may want to understand and adapt the code - but then I use procedure rather than defun (usually). No good reasons for some of these, apart from trying some level of consistency. I never use define to define functions - only to define variables. And I never use defvar at all.

    I've learned that trying to suggest a set of global style guidelines is a thankless task which nobody will agree to (and quite probably I wouldn't agree to them myself!)

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Children
No Data

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information