Fwd: withBreak blocks
Perhaps I'm missing something, but why wouldn't an else if
work here?
jhpratt
else if
would work but it will look kinda nasty if you have deeply nested
validations.
Keep in mind my example was a small and simple.
It's the same as using async
await
vs .then
etc...
Sagiv b.g
This is so close to your proposal, and already works right now:
block: {
if (response.error) {
log(response.message);
break block;
}
if (!response.data) {
log("No data");
break block;
}
if (!response.data.todos) {
log("No Todos");
break block;
}
return action({ data: response.data });
}
Also, you can just use return
if you're in a function:
const doWork = () => {
// try catch omitted for brevity
const response = fetchData();
if (response.error) {
log(response.message);
return;
}
if (!response.data) {
log("No data");
return;
}
if (!response.data.todos) {
log("No Todos");
return;
}
return action({ data: response.data });
};
This is so close to your proposal, and already works right now: Yeah, i just thought labels will become deprecated at some point and never thought of using them. good point though :)
Sagiv b.g
Also, you can just use return if you're in a function:
That's not what i'm looking for. I may want to do some more things inside the function besides validations.
Sagiv b.g
Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?
jhpratt
Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?
The label:
suggestion kinda nails it, though i was sure i read somewhere
that labels should not be used anymore.
Sagiv B.G
Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?
Actually es-lint won't allow it: eslint.org/docs/rules/no-labels
Sagiv B.G
What kind of argument is that? ESlint isn't a JavaScript runtime, it is fully configurable, and I don't see how it's at all relevant.
What kind of argument is that? ESlint isn't a JavaScript runtime, it is fully configurable, and I don't see how it's at all relevant.
I know ESLint can be configured, it was just an example for how label
statements are considered as poor design of code.
Sagiv B.G
Your proposal is conceptually the same as a labelled break statement (ie, GOTO); if you want to follow the advice to avoid labels, I suspect it would apply to your proposal as well.
On Feb 18, 2018, at 4:52 AM, sagiv ben giat <sagiv.bengiat at gmail.com> wrote:
Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?
@sagiv, if you need guidance by use-case/example, here’s a real-world example [1] of a validator “god” function (with 100% code-coverage [2]) that encapsulates most of the logic for validating user-inputs against the full swagger/openapi 2.0 spec [3]. attached screenshot showing how its used from browser-console (works just as well in nodejs).
and yes, the code-sample makes use of break statements in:
- a recursive while loop to dereference schema pointers [4]
- in switch/case blocks which are conceptually similar to what you want to do
[1] kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076, kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076 [2] kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html, kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html [3] OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md, OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md [4] OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject, OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject
/*
* real-world example of swagger-validator from
* https://github.com/kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076
*/
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 100,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
local.validateBySwaggerSchema = function (options) {
/*
* this function will validate options.data against the swagger options.schema
* according to the spec defined at:
* http://json-schema.org/draft-04/json-schema-validation.html#rfc.section.5
*/
var $ref,
circularList,
data,
dataReadonlyRemove2,
ii,
oneOf,
schema,
test,
tmp;
if (!options.schema) {
return;
}
data = options.data;
options.dataReadonlyRemove = options.dataReadonlyRemove || [{}, '', null];
dataReadonlyRemove2 = options.dataReadonlyRemove[2] || {};
schema = options.schema;
circularList = [];
while (true) {
// dereference schema.schema
while (schema.schema) {
schema = schema.schema;
}
// dereference schema.oneOf
oneOf = (data && schema.oneOf) || [];
for (ii = 0; ii < oneOf.length; ii += 1) {
tmp = String(oneOf[ii] && oneOf[ii].$ref)
.replace('http://json-schema.org/draft-04/schema#', '#');
switch (tmp + ' ' + (!local.isNullOrUndefined(data.$ref) || data.in)) {
case '#/definitions/bodyParameter body':
case '#/definitions/formDataParameterSubSchema formData':
case '#/definitions/headerParameterSubSchema header':
case '#/definitions/jsonReference true':
case '#/definitions/pathParameterSubSchema path':
case '#/definitions/queryParameterSubSchema query':
schema = local.swaggerSchemaJson.definitions[tmp.split('/')[2]];
break;
default:
switch (tmp) {
case '#/definitions/bodyParameter':
case '#/definitions/jsonReference':
schema = oneOf[ii ^ 1];
break;
}
}
if (!schema.oneOf) {
break;
}
}
// dereference schema.$ref
$ref = schema && schema.$ref;
if (!$ref) {
break;
}
test = circularList.indexOf($ref) < 0;
local.throwSwaggerError(!test && {
data: data,
errorType: 'schemaDeferenceCircular',
prefix: options.prefix,
schema: schema
});
circularList.push($ref);
tmp = $ref.split('/').slice(-2);
schema = $ref.indexOf('http://json-schema.org/draft-04/schema#/') === 0
? local.swaggerSchemaJson[tmp[0]]
: options.swaggerJson[tmp[0]];
schema = schema && schema[tmp[1]];
test = schema;
local.throwSwaggerError(!test && {
data: data,
errorType: 'schemaDeference',
prefix: options.prefix,
schema: options.schema
});
}
if (options.modeDereference) {
if (options.modeDereferenceDepth > 1) {
schema = local.jsonCopy(schema);
Object.keys(schema.properties || {}).forEach(function (key) {
schema.properties[key] = local.validateBySwaggerSchema({
// dereference property
modeDereference: true,
modeDereferenceDepth: options.modeDereferenceDepth - 1,
prefix: options.prefix.concat(['properties', key]),
schema: schema.properties[key],
swaggerJson: options.swaggerJson
});
});
}
return schema;
}
// validate schema.default
if (options.modeDefault) {
data = schema.default;
}
// validate semanticRequired
test = options.modeDefault ||
!local.isNullOrUndefined(data) ||
schema.required !== true ||
schema['x-swgg-notRequired'];
local.throwSwaggerError(!test && {
data: data,
errorType: 'semanticRequired',
prefix: options.prefix,
schema: schema
});
if (local.isNullOrUndefined(data)) {
return;
}
// validate semanticRequiredArrayItems
test = !options.modeSchema || local.schemaPType(data) !== 'array' ||
(typeof local.schemaPItems(data) === 'object' && local.schemaPItems(data));
local.throwSwaggerError(!test && {
errorType: 'semanticRequiredArrayItems',
prefix: options.prefix,
schema: data
});
// remove readOnly property
if (schema.readOnly) {
delete options.dataReadonlyRemove[0][options.dataReadonlyRemove[1]];
}
// optimization - validate schema.type first
// 5.5.2. type
// https://swagger.io/docs/specification/data-models/data-types/
// https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#data-types
switch (local.schemaPType(schema)) {
case 'array':
test = Array.isArray(data);
break;
case 'boolean':
test = typeof data === 'boolean';
break;
case 'file':
test = !options.modeSchema;
break;
case 'integer':
test = Number.isFinite(data) && Math.floor(data) === data;
switch (schema.format) {
case 'int32':
break;
case 'int64':
break;
}
break;
case 'number':
test = Number.isFinite(data);
switch (schema.format) {
case 'double':
break;
case 'float':
break;
}
break;
case 'string':
test = typeof data === 'string' ||
(!options.modeSchema && schema.format === 'binary');
switch (test && !options.modeSchema && schema.format) {
// Clarify 'byte' format #50
// https://github.com/swagger-api/swagger-spec/issues/50
case 'byte':
test = !(/[^\n\r\+\/0-9\=A-Za-z]/).test(data);
break;
case 'date':
case 'date-time':
test = JSON.stringify(new Date(data)) !== 'null';
break;
case 'email':
test = local.regexpEmailValidate.test(data);
break;
case 'json':
test = local.tryCatchOnError(function () {
JSON.parse(data);
return true;
}, local.nop);
break;
case 'phone':
test = local.regexpPhoneValidate.test(data);
break;
}
break;
default:
test = options.modeSchema || typeof data === 'object';
break;
}
local.throwSwaggerError(!test && {
data: data,
errorType: 'itemType',
prefix: options.prefix,
schema: schema,
typeof: typeof data
});
tmp = typeof data;
if (tmp === 'object' && Array.isArray(data)) {
tmp = 'array';
}
switch (tmp) {
// 5.1. Validation keywords for numeric instances (number and integer)
case 'number':
// 5.1.1. multipleOf
test = typeof schema.multipleOf !== 'number' || data % schema.multipleOf === 0;
local.throwSwaggerError(!test && {
data: data,
errorType: 'numberMultipleOf',
prefix: options.prefix,
schema: schema
});
// 5.1.2. maximum and exclusiveMaximum
test = typeof schema.maximum !== 'number' || (schema.exclusiveMaximum
? data < schema.maximum
: data <= schema.maximum);
local.throwSwaggerError(!test && {
data: data,
errorType: schema.exclusiveMaximum
? 'numberExclusiveMaximum'
: 'numberMaximum',
prefix: options.prefix,
schema: schema
});
// 5.1.3. minimum and exclusiveMinimum
test = typeof schema.minimum !== 'number' || (schema.exclusiveMinimum
? data > schema.minimum
: data >= schema.minimum);
local.throwSwaggerError(!test && {
data: data,
errorType: schema.exclusiveMinimum
? 'numberExclusiveMinimum'
: 'numberMinimum',
prefix: options.prefix,
schema: schema
});
break;
// 5.2. Validation keywords for strings
case 'string':
// 5.2.1. maxLength
test = typeof schema.maxLength !== 'number' || data.length <= schema.maxLength;
local.throwSwaggerError(!test && {
data: data,
errorType: 'stringMaxLength',
prefix: options.prefix,
schema: schema
});
// 5.2.2. minLength
test = typeof schema.minLength !== 'number' || data.length >= schema.minLength;
local.throwSwaggerError(!test && {
data: data,
errorType: 'stringMinLength',
prefix: options.prefix,
schema: schema
});
// 5.2.3. pattern
test = !schema.pattern || new RegExp(schema.pattern).test(data);
local.throwSwaggerError(!test && {
data: data,
errorType: 'stringPattern',
prefix: options.prefix,
schema: schema
});
break;
// 5.3. Validation keywords for arrays
case 'array':
// 5.3.1. additionalItems and items
// swagger disallows array items
data.forEach(function (element, ii) {
// recurse - schema.additionalItems and schema.items
local.validateBySwaggerSchema({
data: element,
dataReadonlyRemove: [dataReadonlyRemove2, ii, dataReadonlyRemove2[ii]],
modeSchema: options.modeSchema,
prefix: options.prefix.concat([ii]),
schema: local.schemaPItems(schema) || schema.additionalItems,
swaggerJson: options.swaggerJson
});
});
// 5.3.2. maxItems
test = typeof schema.maxItems !== 'number' || data.length <= schema.maxItems;
local.throwSwaggerError(!test && {
data: data,
errorType: 'arrayMaxItems',
prefix: options.prefix,
schema: schema
});
// 5.3.3. minItems
test = typeof schema.minItems !== 'number' || data.length >= schema.minItems;
local.throwSwaggerError(!test && {
data: data,
errorType: 'arrayMinItems',
prefix: options.prefix,
schema: schema
});
// 5.3.4. uniqueItems
test = !schema.uniqueItems || data.every(function (element) {
tmp = element;
return data.indexOf(element) === data.lastIndexOf(element);
});
local.throwSwaggerError(!test && {
data: data,
errorType: 'arrayUniqueItems',
prefix: options.prefix,
schema: schema,
tmp: tmp
});
break;
// 5.4. Validation keywords for objects
case 'object':
// 5.4.1. maxProperties
test = typeof schema.maxProperties !== 'number' ||
Object.keys(data).length <= schema.maxProperties;
local.throwSwaggerError(!test && {
data: data,
errorType: 'objectMaxProperties',
prefix: options.prefix,
schema: schema
});
// 5.4.2. minProperties
test = typeof schema.minProperties !== 'number' ||
Object.keys(data).length >= schema.minProperties;
local.throwSwaggerError(!test && {
data: data,
errorType: 'objectMinProperties',
prefix: options.prefix,
schema: schema
});
// 5.4.3. required
local.normalizeValue('list', schema.required).forEach(function (key) {
test = !local.isNullOrUndefined(data[key]);
local.throwSwaggerError(!test && {
data: data,
errorType: 'objectRequired',
key: key,
prefix: options.prefix,
schema: schema
});
});
// 5.4.4. additionalProperties, properties and patternProperties
Object.keys(data).forEach(function (key) {
tmp = null;
if (schema.properties && schema.properties[key]) {
tmp = true;
// recurse - schema.properties
local.validateBySwaggerSchema({
data: data[key],
dataReadonlyRemove: [
dataReadonlyRemove2,
key,
dataReadonlyRemove2[key]
],
modeSchema: options.modeSchema,
prefix: options.prefix.concat([key]),
schema: schema.properties[key],
swaggerJson: options.swaggerJson
});
}
Object.keys(schema.patternProperties || {}).forEach(function (rgx) {
if (new RegExp(rgx).test(key)) {
tmp = true;
// recurse - schema.patternProperties
local.validateBySwaggerSchema({
data: data[key],
modeSchema: options.modeSchema,
prefix: options.prefix.concat([key]),
schema: schema.patternProperties[rgx],
swaggerJson: options.swaggerJson
});
}
});
/*
* validate
* 5.4.4.4. If "additionalProperties" has boolean value false
*
* In this case, validation of the instance depends on the property set of
* "properties" and "patternProperties". In this section, the property names of
* "patternProperties" will be called regexes for convenience.
*
* The first step is to collect the following sets:
*
* s
* The property set of the instance to validate.
* p
* The property set from "properties".
* pp
* The property set from "patternProperties".
* Having collected these three sets, the process is as follows:
*
* remove from "s" all elements of "p", if any;
* for each regex in "pp", remove all elements of "s" which this regex matches.
* Validation of the instance succeeds if, after these two steps, set "s" is empty.
*/
test = tmp || schema.additionalProperties !== false;
local.throwSwaggerError(!test && {
data: data,
errorType: 'objectAdditionalProperties',
key: key,
prefix: options.prefix,
schema: schema
});
// recurse - schema.additionalProperties
local.validateBySwaggerSchema({
data: data[key],
modeSchema: options.modeSchema,
prefix: options.prefix.concat([key]),
schema: schema.additionalProperties,
swaggerJson: options.swaggerJson
});
});
// 5.4.5. dependencies
Object.keys(schema.dependencies || {}).forEach(function (key) {
if (local.isNullOrUndefined(data[key])) {
return;
}
// 5.4.5.2.1. Schema dependencies
// recurse - schema.dependencies
local.validateBySwaggerSchema({
data: data[key],
modeSchema: options.modeSchema,
prefix: options.prefix.concat([key]),
schema: schema.dependencies[key],
swaggerJson: options.swaggerJson
});
// 5.4.5.2.2. Property dependencies
local.normalizeValue('list', schema.dependencies[key]).every(function (key2) {
test = !local.isNullOrUndefined(data[key2]);
local.throwSwaggerError(!test && {
data: data,
errorType: 'objectDependencies',
key: key,
key2: key2,
prefix: options.prefix,
schema: schema
});
});
});
break;
}
// 5.5. Validation keywords for any instance type
// 5.5.1. enum
tmp = schema.enum || (!options.modeSchema && (local.schemaPItems(schema) || {}).enum);
test = !tmp || (Array.isArray(data)
? data
: [data]).every(function (element) {
return tmp.indexOf(element) >= 0;
});
local.throwSwaggerError(!test && {
data: data,
errorType: 'itemEnum',
prefix: options.prefix,
schema: schema,
tmp: tmp
});
// 5.5.2. type
local.nop();
// 5.5.3. allOf
(schema.allOf || []).forEach(function (element) {
// recurse - schema.allOf
local.validateBySwaggerSchema({
data: data,
prefix: options.prefix,
modeSchema: options.modeSchema,
schema: element,
swaggerJson: options.swaggerJson
});
});
// 5.5.4. anyOf
tmp = null;
test = !schema.anyOf || schema.anyOf.some(function (element) {
local.tryCatchOnError(function () {
// recurse - schema.anyOf
local.validateBySwaggerSchema({
data: data,
modeSchema: options.modeSchema,
prefix: options.prefix,
schema: element,
swaggerJson: options.swaggerJson
});
return true;
}, local.nop);
tmp = tmp || local.utility2._debugTryCatchError;
return !tmp;
});
local.throwSwaggerError(!test && {
data: data,
errorType: 'itemOneOf',
prefix: options.prefix,
schema: schema,
tmp: tmp
});
// 5.5.5. oneOf
tmp = !schema.oneOf
? 1
: 0;
(schema.oneOf || []).some(function (element) {
local.tryCatchOnError(function () {
// recurse - schema.oneOf
local.validateBySwaggerSchema({
data: data,
modeSchema: options.modeSchema,
prefix: options.prefix,
schema: element,
swaggerJson: options.swaggerJson
});
tmp += 1;
}, local.nop);
return tmp > 1;
});
test = tmp === 1;
local.throwSwaggerError(!test && {
data: data,
errorType: 'itemOneOf',
prefix: options.prefix,
schema: schema,
tmp: tmp
});
// 5.5.6. not
test = !schema.not || !local.tryCatchOnError(function () {
// recurse - schema.not
local.validateBySwaggerSchema({
data: data,
modeSchema: options.modeSchema,
prefix: options.prefix,
schema: schema.not,
swaggerJson: options.swaggerJson
});
return true;
}, local.nop);
local.throwSwaggerError(!test && {
data: data,
errorType: 'itemNot',
prefix: options.prefix,
schema: schema
});
// 5.5.7. definitions
local.nop();
// validate data.$ref
if (schema === local.swaggerSchemaJson.definitions.jsonReference) {
local.validateBySwaggerSchema({
modeDereference: true,
modeSchema: options.modeSchema,
prefix: options.prefix,
schema: data,
swaggerJson: options.swaggerJson
});
}
return schema;
};
/*
* output from running code inside browser-console
*/
var mySchema = {
required: ['myBoolean'],
properties: {
myArrayOfStrings: { items: { type: 'string' }, type: 'array' },
myBoolean: { type: 'boolean' },
myNumber: { type: 'number' },
myString: { enum: ['hello world', 'bye world'], type: 'string' }
}
};
undefined
local.validateBySwaggerSchema({
data: {
myArrayOfStrings: ['foo', 'bar'],
myBoolean: false,
myString: 'hello world'
},
prefix: ['myData'],
schema: mySchema
});
{required: Array(1), properties: {…}}
local.validateBySwaggerSchema({
data: {
myArrayOfStrings: [1, 2],
myBoolean: false
},
prefix: ['myData'],
schema: mySchema
});
assets.utility2.rollup.js:26060 Uncaught Error: error.itemType - value myData["myArrayOfStrings"][0] = 1 is not a valid string
at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27017)
at assets.utility2.rollup.js:27097
at Array.forEach (<anonymous>)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27095)
at assets.utility2.rollup.js:27172
at Array.forEach (<anonymous>)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27017
(anonymous) @ assets.utility2.rollup.js:27097
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27095
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM341:2
local.validateBySwaggerSchema({
data: {
myArrayOfStrings: ['foo', 'bar'],
myBoolean: null
},
prefix: ['myData'],
schema: mySchema
});
assets.utility2.rollup.js:26060 Uncaught Error: error.objectRequired - object myData = {"myArrayOfStrings":["foo","bar"],"myBoolean":null} must have property "myBoolean"
at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
at assets.utility2.rollup.js:27158
at Array.forEach (<anonymous>)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27156)
at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
(anonymous) @ assets.utility2.rollup.js:27158
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27156
(anonymous) @ VM343:2
local.validateBySwaggerSchema({
data: {
myBoolean: false,
myString: 'hello undefined'
},
prefix: ['myData'],
schema: mySchema
});
assets.utility2.rollup.js:26060 Uncaught Error: error.itemEnum - string myData["myString"] = "hello undefined" can only have items from the list ["hello world","bye world"]
at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27274)
at assets.utility2.rollup.js:27172
at Array.forEach (<anonymous>)
at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
at <anonymous>:2:7
local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27274
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM345:2
No if / else if / else or nested if blocks:
// Simulated fallible fetchData
const fetchData = () => {
const x = Math.random();
if (x < 0.25) return {error: true, message: "fetchData error"};
if (x < 0.50) return {};
if (x < 0.75) return {data: {}};
return {data: {todos: ["refactor your code"]}};
};
// Simulated action
const action = o => log(o.data);
const checkForError = response => {
const isError = response.error;
if (isError) {
log(response.message);
}
return isError;
};
const checkForNoData = response => {
const isNoData = !response.data;
if (isNoData) {
log("No data");
}
return isNoData;
};
const checkForNoTodos = response => {
const isNoTodos = !response.data.todos;
if (isNoTodos) {
log("No Todos");
}
return isNoTodos;
};
const checkList = [
checkForError,
checkForNoData,
checkForNoTodos
];
const doWork = () => {
const response = fetchData();
const checkFailed = checkList.find(check => check(response));
if (!checkFailed) {
return action({data: response.data});
}
};
Kai, it's unlikely anyone will read all that code! Better to illustrate your point with minimal code snippets
As previously mentioned, this is effectively already covered by
labeled blocks. They aren't slated for removal (some things are just
nearly impossible to express without them), just they have a tendency
to get abused like goto
in C - people have this tendency in C/C++ to
get a little over-eager to reach for that shotgun when a
while
/for
/if
/etc. would do. So a strong discouragement is
generally warranted, but mainly to get less-skilled coders to not code
themselves into an unmaintainable hole before they can properly wield
the tool and understand why it exists. (It makes for a nice way to
break from non-trivial logic when a separate function would only serve
to complicate the code.)
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
I hope I'm on the right medium for this, I would like to propose a language feature. withBreak blocks
Well the name might not be the best, but it's just my way to be the most clear about this feature proposal.
I often find my self doing this in my code:
I'm doing this instead of doing bunch of if / else if / else blocks or ugly nested if blocks. What i would like to have is a block that will let me break without being in a loop context. Something like this:
This can be a synthetic sugar for do{}while(false) behind the scenes.
Best ,
Sagiv B.G